From c59197ebe92179f265767b96fced7ace31138897 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 19:55:38 +0800 Subject: [PATCH 01/56] Delete 'src/bin/gs_cgroup/cgconf.cpp' --- src/bin/gs_cgroup/cgconf.cpp | 1683 ---------------------------------- 1 file changed, 1683 deletions(-) delete mode 100644 src/bin/gs_cgroup/cgconf.cpp diff --git a/src/bin/gs_cgroup/cgconf.cpp b/src/bin/gs_cgroup/cgconf.cpp deleted file mode 100644 index a09ae2768..000000000 --- a/src/bin/gs_cgroup/cgconf.cpp +++ /dev/null @@ -1,1683 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * cgconf.cpp - * Cgroup configration file process functions - * - * IDENTIFICATION - * src/bin/gs_cgroup/cgconf.cpp - * - * ------------------------------------------------------------------------- - */ -#include -#include -#include -#include /* stat */ -#include -#include /* mmap */ -#include - -#include "securec.h" -#include "cgutil.h" - -/* - ***************** STATIC FUNCTIONS ************************ - */ - -/* - * function name: cgconf_get_group_type - * description : get the string of group type - * arguments : group type enum type value - * return value : the string of group type - */ -char* cgconf_get_group_type(group_type gtype) -{ - if (gtype == GROUP_TOP) - return "Top"; - else if (gtype == GROUP_CLASS) - return "CLASS"; - else if (gtype == GROUP_BAKWD) - return "BAKWD"; - else if (gtype == GROUP_DEFWD) - return "DEFWD"; - else if (gtype == GROUP_TSWD) - return "TSWD"; - - return NULL; -} - -/* - * function name: cgconf_set_root_group - * description : set the default value of root group in configuration file - * - * Note: The root group can't set the IO relative weight. - * The percentage is calculated based on 1000. - */ -static void cgconf_set_root_group(void) -{ - errno_t sret; - cgutil_vaddr[TOPCG_ROOT]->used = 1; - cgutil_vaddr[TOPCG_ROOT]->gid = TOPCG_ROOT; - cgutil_vaddr[TOPCG_ROOT]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_ROOT]->grpname, GPNAME_LEN, GSCGROUP_ROOT, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = 100 * DEFAULT_IO_WEIGHT / MAX_IO_WEIGHT; - cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = DEFAULT_IO_WEIGHT; - cgutil_vaddr[TOPCG_ROOT]->percent = 1000; - - /* set root group as default cpu set */ - sret = snprintf_s(cgutil_vaddr[TOPCG_ROOT]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); - securec_check_intval(sret, , ); -} - -/* - * function name: cgconf_set_gauss_group - * description : set the default value of Gaussdb group in configuration file - * - * Note: The IO weight value is set as 1000 (MAX_IO_WEIGHT). - * It supposes that the gaussdb can use the maximum IO resource. - * The percentage is calculated based on CPU shares value. - */ -static void cgconf_set_gauss_group(void) -{ - errno_t rc; - - cgutil_vaddr[TOPCG_GAUSSDB]->used = 1; - cgutil_vaddr[TOPCG_GAUSSDB]->gid = TOPCG_GAUSSDB; - cgutil_vaddr[TOPCG_GAUSSDB]->gtype = GROUP_TOP; - if ('\0' == cgutil_opt.nodegroup[0]) - rc = snprintf_s(cgutil_vaddr[TOPCG_GAUSSDB]->grpname, - GPNAME_LEN, - GPNAME_LEN - 1, - "%s:%s", - GSCGROUP_TOP_DATABASE, - cgutil_opt.user); - else - rc = snprintf_s(cgutil_vaddr[TOPCG_GAUSSDB]->grpname, - GPNAME_LEN, - GPNAME_LEN - 1, - "%s:%s", - GSCGROUP_TOP_DATABASE, - cgutil_passwd_user->pw_name); - securec_check_intval(rc, , ); - cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent = - 100 * DEFAULT_GAUSS_CPUSHARES / (DEFAULT_CPU_SHARES + DEFAULT_GAUSS_CPUSHARES); - cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.shares = DEFAULT_GAUSS_CPUSHARES; - cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.weight = MAX_IO_WEIGHT; - cgutil_vaddr[TOPCG_GAUSSDB]->percent = - cgutil_vaddr[TOPCG_ROOT]->percent * DEFAULT_GAUSS_CPUSHARES / (DEFAULT_CPU_SHARES + DEFAULT_GAUSS_CPUSHARES); - - /* set root group as root group cpu set */ - if (*cgutil_vaddr[TOPCG_GAUSSDB]->cpuset == '\0') { - rc = snprintf_s( - cgutil_vaddr[TOPCG_GAUSSDB]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_ROOT]->cpuset); - securec_check_intval(rc, , ); - } -} - -/* - * function name: cgconf_set_top_backend_group - * description : set the default value of Top Backend group - * - */ -static void cgconf_set_top_backend_group(void) -{ - errno_t sret; - cgutil_vaddr[TOPCG_BACKEND]->used = 1; - cgutil_vaddr[TOPCG_BACKEND]->gid = TOPCG_BACKEND; - cgutil_vaddr[TOPCG_BACKEND]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_BACKEND]->grpname, GPNAME_LEN, GSCGROUP_TOP_BACKEND, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = TOP_BACKEND_PERCENT; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_BACKEND_PERCENT / 10; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_BACKEND_PERCENT); - cgutil_vaddr[TOPCG_BACKEND]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_BACKEND_PERCENT / 100; - - /* set root group as gaussdb group cpu set */ - if (*cgutil_vaddr[TOPCG_BACKEND]->cpuset == '\0') { - sret = snprintf_s( - cgutil_vaddr[TOPCG_BACKEND]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); - securec_check_intval(sret, , ); - } -} - -/* - * function name: cgconf_set_top_class_group - * description : set the default value of Top Class group - * - */ -static void cgconf_set_top_class_group(void) -{ - errno_t sret; - cgutil_vaddr[TOPCG_CLASS]->used = 1; - cgutil_vaddr[TOPCG_CLASS]->gid = TOPCG_CLASS; - cgutil_vaddr[TOPCG_CLASS]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_CLASS]->grpname, GPNAME_LEN, GSCGROUP_TOP_CLASS, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = TOP_CLASS_PERCENT; - cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10; - cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT); - cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100; - /* set root group as gaussdb group cpu set */ - if (*cgutil_vaddr[TOPCG_CLASS]->cpuset == '\0') { - sret = snprintf_s( - cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); - securec_check_intval(sret, , ); - } -} - -/* - * function name: cgconf_set_nodegroup_top_group - * description : set the default value of Nodegroup Top group - * - */ -static void cgconf_set_nodegroup_top_group(void) -{ - errno_t sret; - cgutil_vaddr[TOPCG_CLASS]->used = 1; - cgutil_vaddr[TOPCG_CLASS]->gid = TOPCG_CLASS; - cgutil_vaddr[TOPCG_CLASS]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_CLASS]->grpname, GPNAME_LEN, cgutil_opt.nodegroup, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = TOP_CLASS_PERCENT; - cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10; - cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT); - cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100; - /* set root group as gaussdb group cpu set */ - if (*cgutil_vaddr[TOPCG_CLASS]->cpuset == '\0') { - sret = snprintf_s( - cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); - securec_check_intval(sret, , ); - } -} - -/* - * function name: cgconf_set_default_backend_group - * description : set the default value of default backend group - * - */ -void cgconf_set_default_backend_group(void) -{ - errno_t sret; - cgutil_vaddr[BACKENDCG_START_ID]->used = 1; - cgutil_vaddr[BACKENDCG_START_ID]->gid = BACKENDCG_START_ID; - cgutil_vaddr[BACKENDCG_START_ID]->gtype = GROUP_BAKWD; - cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.tgid = TOPCG_BACKEND; - cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.percent = DEFAULT_BACKEND_PERCENT; - sret = strncpy_s(cgutil_vaddr[BACKENDCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_BACKEND, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[BACKENDCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_BACKEND_PERCENT / 10; - cgutil_vaddr[BACKENDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_BACKEND_PERCENT); - cgutil_vaddr[BACKENDCG_START_ID]->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * DEFAULT_BACKEND_PERCENT / 100; - - /* set root group as backend group cpu set */ - if (*cgutil_vaddr[BACKENDCG_START_ID]->cpuset == '\0') { - sret = snprintf_s(cgutil_vaddr[BACKENDCG_START_ID]->cpuset, - CPUSET_LEN, - CPUSET_LEN - 1, - "%s", - cgutil_vaddr[TOPCG_BACKEND]->cpuset); - securec_check_intval(sret, , ); - } -} - -/* - * function name: cgconf_set_vacuum_group - * description : set the default value of vacuum backend group - * - */ -void cgconf_set_vacuum_group(void) -{ - errno_t sret; - cgutil_vaddr[BACKENDCG_START_ID + 1]->used = 1; - cgutil_vaddr[BACKENDCG_START_ID + 1]->gid = BACKENDCG_START_ID + 1; - cgutil_vaddr[BACKENDCG_START_ID + 1]->gtype = GROUP_BAKWD; - cgutil_vaddr[BACKENDCG_START_ID + 1]->ginfo.cls.tgid = TOPCG_BACKEND; - cgutil_vaddr[BACKENDCG_START_ID + 1]->ginfo.cls.percent = VACUUM_PERCENT; - sret = strncpy_s(cgutil_vaddr[BACKENDCG_START_ID + 1]->grpname, GPNAME_LEN, GSCGROUP_VACUUM, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[BACKENDCG_START_ID + 1]->ainfo.shares = DEFAULT_CPU_SHARES * VACUUM_PERCENT / 10; - cgutil_vaddr[BACKENDCG_START_ID + 1]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, VACUUM_PERCENT); - cgutil_vaddr[BACKENDCG_START_ID + 1]->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * VACUUM_PERCENT / 100; - /* set root group as backend group cpu set */ - if (*cgutil_vaddr[BACKENDCG_START_ID + 1]->cpuset == '\0') { - sret = snprintf_s(cgutil_vaddr[BACKENDCG_START_ID + 1]->cpuset, - CPUSET_LEN, - CPUSET_LEN - 1, - "%s", - cgutil_vaddr[TOPCG_BACKEND]->cpuset); - securec_check_intval(sret, , ); - } -} - -/* - * function name: cgconf_set_default_class_group - * description : set the default value of default class group - * - */ -void cgconf_set_default_class_group(void) -{ - errno_t sret; - cgutil_vaddr[CLASSCG_START_ID]->used = 1; - cgutil_vaddr[CLASSCG_START_ID]->gid = CLASSCG_START_ID; - cgutil_vaddr[CLASSCG_START_ID]->gtype = GROUP_CLASS; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.tgid = TOPCG_CLASS; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.maxlevel = 1; /* initialized value */ - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.percent = DEFAULT_CLASS_PERCENT; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.rempct = 100; /* initialized value */ - sret = strncpy_s(cgutil_vaddr[CLASSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_CLASS, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[CLASSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_CLASS_PERCENT / 10; - cgutil_vaddr[CLASSCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_CLASS_PERCENT); - /* it has only this class, so it has all resource */ - cgutil_vaddr[CLASSCG_START_ID]->percent = cgutil_vaddr[TOPCG_CLASS]->percent; - - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].skewpercent = DEFAULT_CPUSKEWPCT; - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].qualitime = DEFAULT_QUALITIME; -} - -/* - * function name: cgconf_set_default_top_workload_group - * description : set the default value of top workload group - * - */ -static void cgconf_set_default_top_workload_group(void) -{ - char tmpstr[GPNAME_LEN]; - errno_t sret; - - cgutil_vaddr[WDCG_START_ID]->used = 1; - cgutil_vaddr[WDCG_START_ID]->gid = WDCG_START_ID; - cgutil_vaddr[WDCG_START_ID]->gtype = GROUP_DEFWD; - cgutil_vaddr[WDCG_START_ID]->ginfo.wd.cgid = CLASSCG_START_ID; - cgutil_vaddr[WDCG_START_ID]->ginfo.wd.wdlevel = 1; - sret = snprintf_s(tmpstr, sizeof(tmpstr), sizeof(tmpstr) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); - securec_check_intval(sret, , ); - - sret = strncpy_s(cgutil_vaddr[WDCG_START_ID]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - - cgutil_vaddr[WDCG_START_ID]->ainfo.shares = MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100; - cgutil_vaddr[WDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT); -} - -/* - * function name: cgconf_set_default_timeshare_group - * description : set the default value of default timeshare group - * - */ -static void cgconf_set_default_timeshare_group(void) -{ - errno_t sret; - /* low group of default group */ - cgutil_vaddr[TSCG_START_ID]->used = 1; - cgutil_vaddr[TSCG_START_ID]->gid = TSCG_START_ID; - cgutil_vaddr[TSCG_START_ID]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID]->ginfo.ts.rate = TS_LOW_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_LOW_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * TS_LOW_RATE; - cgutil_vaddr[TSCG_START_ID]->ainfo.weight = MIN_IO_WEIGHT * TS_LOW_RATE; - /* medium group of default group */ - cgutil_vaddr[TSCG_START_ID + 1]->used = 1; - cgutil_vaddr[TSCG_START_ID + 1]->gid = TSCG_START_ID + 1; - cgutil_vaddr[TSCG_START_ID + 1]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID + 1]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID + 1]->ginfo.ts.rate = TS_MEDIUM_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 1]->grpname, GPNAME_LEN, GSCGROUP_MEDIUM_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID + 1]->ainfo.shares = DEFAULT_CPU_SHARES * TS_MEDIUM_RATE; - cgutil_vaddr[TSCG_START_ID + 1]->ainfo.weight = MIN_IO_WEIGHT * TS_MEDIUM_RATE; - /* high group of default group */ - cgutil_vaddr[TSCG_START_ID + 2]->used = 1; - cgutil_vaddr[TSCG_START_ID + 2]->gid = TSCG_START_ID + 2; - cgutil_vaddr[TSCG_START_ID + 2]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID + 2]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID + 2]->ginfo.ts.rate = TS_HIGH_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 2]->grpname, GPNAME_LEN, GSCGROUP_HIGH_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID + 2]->ainfo.shares = DEFAULT_CPU_SHARES * TS_HIGH_RATE; - cgutil_vaddr[TSCG_START_ID + 2]->ainfo.weight = MIN_IO_WEIGHT * TS_HIGH_RATE; - /* rush group of default group */ - cgutil_vaddr[TSCG_START_ID + 3]->used = 1; - cgutil_vaddr[TSCG_START_ID + 3]->gid = TSCG_START_ID + 3; - cgutil_vaddr[TSCG_START_ID + 3]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID + 3]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID + 3]->ginfo.ts.rate = TS_RUSH_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 3]->grpname, GPNAME_LEN, GSCGROUP_RUSH_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID + 3]->ainfo.shares = DEFAULT_CPU_SHARES * TS_RUSH_RATE; - cgutil_vaddr[TSCG_START_ID + 3]->ainfo.weight = MIN_IO_WEIGHT * TS_RUSH_RATE; -} - -/* - * @Description: reset cgroup configure. - * @Return: void - * @See also: - */ -void cgconf_reset_cgroup_config(void) -{ - /* create the default top group */ - cgconf_set_root_group(); - cgconf_set_gauss_group(); - cgconf_set_top_backend_group(); - - /* create the default vacuum group under top backend group */ - cgconf_set_default_backend_group(); - cgconf_set_vacuum_group(); - - if (cgutil_opt.nodegroup[0] == '\0' || cgutil_opt.rename) { - cgconf_set_top_class_group(); - } else { - /* create the nodegroup top group */ - cgconf_set_nodegroup_top_group(); - } - - /* create the default class group under top class group */ - cgconf_set_default_class_group(); - cgconf_set_default_top_workload_group(); - - /* create the top/rush/high/medium/low timeshare group of - default class group */ - cgconf_set_default_timeshare_group(); -} -/* - * @Description: revert io configure. - * @IN iovalue: iovalue to be reverted - * @Return: void - * @See also: - */ -void cgconf_revert_blkio_value(char* iovalue) -{ - char *p = NULL; - char *q = NULL; - char *head = NULL; - char *i = NULL; - errno_t sret; - - if ((head = strdup(iovalue)) == NULL) { - fprintf(stderr, "revert blkio failed, cannot alloc memory."); - return; - } - - sret = memset_s(iovalue, IODATA_LEN, 0, IODATA_LEN); - securec_check_errno(sret, free(head), ); - - p = head; - do { - q = strchr(p, '\n'); - if (q != NULL) { - *q++ = '\0'; - } - i = p; - while (*i++) { - if (*i == '\t') { - *i = ' '; - break; - } - } - i++; - /* - * set the blkio throttle values (iopsread/iopswrite/bpsread/bpswrite) - * of the device to 0 this device will be reverted. - */ - *i = '0'; - - while (*i++) { - *i = '\0'; - } - if (iovalue[0]) { - sret = sprintf_s(iovalue + strlen(iovalue), IODATA_LEN - strlen(iovalue), "\n%s", p); - securec_check_intval(sret, free(head), ); - } else { - sret = sprintf_s(iovalue, IODATA_LEN, "%s", p); - securec_check_intval(sret, free(head), ); - } - p = q; - } while (q != NULL); - free(head); - head = NULL; -} -/* - * @Description: revert configure file. - * @IN void - * @Return: void - * @See also: - */ -void cgconf_revert_config_file(void) -{ - int i = 0; - - /* get current user name */ - errno_t sret = snprintf_s( - cgutil_opt.user, sizeof(cgutil_opt.user), sizeof(cgutil_opt.user) - 1, "%s", cgutil_passwd_user->pw_name); - securec_check_intval(sret, , ); - - for (i = 1; i < GSCGROUP_ALLNUM; ++i) { - cgutil_vaddr[i]->used = 0; - *cgutil_vaddr[i]->cpuset = '\0'; - cgutil_vaddr[i]->ainfo.quota = 0; - if (cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) { - if (cgutil_vaddr[i]->ainfo.iopsread[0]) { - cgconf_revert_blkio_value(cgutil_vaddr[i]->ainfo.iopsread); - } - - if (cgutil_vaddr[i]->ainfo.iopswrite[0]) { - cgconf_revert_blkio_value(cgutil_vaddr[i]->ainfo.iopswrite); - } - - if (cgutil_vaddr[i]->ainfo.bpsread[0]) { - cgconf_revert_blkio_value(cgutil_vaddr[i]->ainfo.bpsread); - } - - if (cgutil_vaddr[i]->ainfo.bpswrite[0]) { - cgconf_revert_blkio_value(cgutil_vaddr[i]->ainfo.bpswrite); - } - } - } - - /* reset configure */ - cgconf_reset_cgroup_config(); -} - -/* - * function name: cgconf_generate_default_config_file - * description : generate the default configuration file - * - */ -void cgconf_generate_default_config_file(void* vaddr) -{ - int i = 0; - - for (i = 0; i < GSCGROUP_ALLNUM; i++) { - cgutil_vaddr[i] = (gscgroup_grp_t*)vaddr + i; - cgutil_vaddr[i]->used = 0; - } - - /* reset configure */ - cgconf_reset_cgroup_config(); -} - -/* - * function name: cgconf_update_backend_percent - * description : update the percentage value of backend group - * Note: this function is called after updating Backend group value - */ -void cgconf_update_backend_percent(void) -{ - int i; - int percent = 0; - - /* get the used percent of all backend */ - for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used) { - percent += cgutil_vaddr[i]->ginfo.cls.percent; - } - } - - /* update the percent of each backend */ - for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used) { - if (percent == 0) { - fprintf(stderr, "ERROR: the percentage value of backend group is zero.\n"); - continue; - } - cgutil_vaddr[i]->percent = - cgutil_vaddr[TOPCG_BACKEND]->percent * cgutil_vaddr[i]->ginfo.cls.percent / percent; - - } - } -} - -/* - * function name: cgconf_update_backend_group - * description : reset the percent and calculate the CPU shares and IO weight - of the specified group - * argument : the data structure of backend group - * - * Note: this function is called after updating Backend group value - */ -void cgconf_update_backend_group(gscgroup_grp_t* grp) -{ - grp->ginfo.cls.percent = cgutil_opt.bkdpct; - - grp->ainfo.shares = DEFAULT_CPU_SHARES * grp->ginfo.cls.percent / 10; - - grp->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, grp->ginfo.cls.percent); - - grp->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * grp->ginfo.cls.percent / 100; -} - -/* - * function name: cgconf_update_class_percent - * description : update the percentage value of all class group and - its all workload group - * - * Note: this function is called after updating Class group value - */ -void cgconf_update_class_percent(void) -{ - int i; - int j; - int percent = 0; - - /* get total percent of all class group */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used) - percent += cgutil_vaddr[i]->ginfo.cls.percent; - } - - /* update the percentage of class and workload group */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used) { - if (percent == 0) { - continue; - } - cgutil_vaddr[i]->percent = - cgutil_vaddr[TOPCG_CLASS]->percent * cgutil_vaddr[i]->ginfo.cls.percent / percent; - - if (cgutil_vaddr[i]->percent == 0) - cgutil_vaddr[i]->percent = 1; - } - - for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { - if (cgutil_vaddr[j]->used == 0 || cgutil_vaddr[j]->ginfo.wd.cgid != cgutil_vaddr[i]->gid) - continue; - - cgutil_vaddr[j]->percent = cgutil_vaddr[i]->percent * cgutil_vaddr[j]->ginfo.wd.percent / 100; - - if (cgutil_vaddr[j]->percent == 0) - cgutil_vaddr[j]->percent = 1; - } - } -} - -/* - * function name: cgconf_update_top_percent - * description : update the percentage value of all Backend and Class group - * - * Note: this function is called after updating Gaussdb group value - */ -void cgconf_update_top_percent(void) -{ - cgutil_vaddr[TOPCG_BACKEND]->percent = - cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent / 100; - cgconf_update_backend_percent(); - - cgutil_vaddr[TOPCG_CLASS]->percent = - cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent / 100; - cgconf_update_class_percent(); -} - -/* - * function name: cgconf_set_class_group - * description : set the class group fields when creating new class - * - * Note: this function is called after creating new Class group - */ -void cgconf_set_class_group(int gid) -{ - errno_t sret; - cgutil_vaddr[gid]->used = 1; - cgutil_vaddr[gid]->gid = gid; - cgutil_vaddr[gid]->gtype = GROUP_CLASS; - cgutil_vaddr[gid]->ginfo.cls.tgid = TOPCG_CLASS; - cgutil_vaddr[gid]->ginfo.cls.maxlevel = 0; - - cgutil_vaddr[gid]->ginfo.cls.percent = cgutil_opt.clspct; - cgutil_vaddr[gid]->ginfo.cls.rempct = 100; - - sret = strncpy_s(cgutil_vaddr[gid]->grpname, GPNAME_LEN, cgutil_opt.clsname, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - - cgutil_vaddr[gid]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_vaddr[gid]->ginfo.cls.percent / 10; - - cgutil_vaddr[gid]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_vaddr[gid]->ginfo.cls.percent); - - sret = snprintf_s(cgutil_vaddr[gid]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_CLASS]->cpuset); - - securec_check_intval(sret, , ); - - cgconf_update_class_percent(); -} - -/* - * function name: cgconf_reset_class_group - * description : reset the class group fields when dropping a class - * - * Note: this function is called after dropping a Class group - */ -void cgconf_reset_class_group(int gid) -{ - int i; - errno_t sret; - - for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (cgutil_vaddr[i]->ginfo.wd.cgid == gid) { - sret = memset_s(cgutil_vaddr[i], sizeof(gscgroup_grp_t), 0, sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , ); - } - } - - sret = memset_s(cgutil_vaddr[gid], sizeof(gscgroup_grp_t), 0, sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , ); - - cgconf_update_class_percent(); -} - -/* - * function name: cgconf_update_class_group - * description : update the class group value - * - * Note: this function is called after updating Class group - */ -void cgconf_update_class_group(gscgroup_grp_t* grp) -{ - grp->ginfo.cls.percent = cgutil_opt.clspct; - - grp->ainfo.shares = DEFAULT_CPU_SHARES * grp->ginfo.cls.percent / 10; - - grp->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, grp->ginfo.cls.percent); - - cgconf_update_class_percent(); -} - -/* - * function name: cgconf_set_top_workload_group - * description : set the default value of top workload group - * - */ -void cgconf_set_top_workload_group(int wdgid, int clsgid) -{ - char tmpstr[GPNAME_LEN]; - errno_t sret; - - cgutil_vaddr[wdgid]->used = 1; - cgutil_vaddr[wdgid]->gid = wdgid; - cgutil_vaddr[wdgid]->gtype = GROUP_DEFWD; - cgutil_vaddr[wdgid]->ginfo.wd.cgid = clsgid; - cgutil_vaddr[wdgid]->ginfo.wd.wdlevel = 1; - cgutil_vaddr[wdgid]->ginfo.wd.percent = TOPWD_PERCENT; - sret = snprintf_s(tmpstr, sizeof(tmpstr), sizeof(tmpstr) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); - securec_check_intval(sret, , ); - - sret = strncpy_s(cgutil_vaddr[wdgid]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - - cgutil_vaddr[wdgid]->ainfo.shares = MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100; - cgutil_vaddr[wdgid]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT); - cgutil_vaddr[wdgid]->percent = cgutil_vaddr[clsgid]->percent * cgutil_vaddr[wdgid]->ginfo.wd.percent / 100; - /* set it's cpuset in configure file */ - sret = snprintf_s(cgutil_vaddr[wdgid]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[clsgid]->cpuset); - securec_check_intval(sret, , ); -} - -/* - * function name: cgconf_set_workload_group - * description : set the workload group fields when creating new workload group - * - * Note: this function is called after creating new Workload group - */ -void cgconf_set_workload_group(int wdgid, int clsgid) -{ - char tmpstr[GPNAME_LEN]; - - errno_t sret; - - cgutil_vaddr[wdgid]->used = 1; - cgutil_vaddr[wdgid]->gid = wdgid; - cgutil_vaddr[wdgid]->gtype = GROUP_DEFWD; - cgutil_vaddr[wdgid]->ginfo.wd.cgid = clsgid; - cgutil_vaddr[wdgid]->ginfo.wd.wdlevel = cgutil_vaddr[clsgid]->ginfo.cls.maxlevel + 1; - cgutil_vaddr[wdgid]->ginfo.wd.percent = cgutil_opt.grppct; - cgutil_vaddr[clsgid]->ginfo.cls.rempct -= cgutil_opt.grppct; - sret = snprintf_s( - tmpstr, sizeof(tmpstr), sizeof(tmpstr) - 1, "%s:%d", cgutil_opt.wdname, cgutil_vaddr[wdgid]->ginfo.wd.wdlevel); - securec_check_intval(sret, , ); - sret = strncpy_s(cgutil_vaddr[wdgid]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); - securec_check_intval(sret, , ); - - cgutil_vaddr[wdgid]->ainfo.shares = MAX_CLASS_CPUSHARES * cgutil_vaddr[wdgid]->ginfo.wd.percent / 100; - cgutil_vaddr[wdgid]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_vaddr[wdgid]->ginfo.wd.percent); - cgutil_vaddr[wdgid]->percent = cgutil_vaddr[clsgid]->percent * cgutil_vaddr[wdgid]->ginfo.wd.percent / 100; - - sret = snprintf_s(cgutil_vaddr[wdgid]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[clsgid]->cpuset); - - securec_check_intval(sret, , ); -} - -/* - * function name: cgconf_reset_workload_group - * description : reset the workload group fields when dropping a workload - * - * Note: this function is called after dropping a Workload group - */ -void cgconf_reset_workload_group(int wdgid) -{ - errno_t sret; - sret = memset_s(cgutil_vaddr[wdgid], sizeof(gscgroup_grp_t), 0, sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , ); -} - -/* - * function name: cgconf_update_workload_group - * description : update the workload group value - * Note: this function is called after updating Workload group - */ -void cgconf_update_workload_group(gscgroup_grp_t* grp) -{ - int clsgid = grp->ginfo.wd.cgid; - - cgutil_vaddr[clsgid]->ginfo.cls.rempct += grp->ginfo.wd.percent; - grp->ginfo.wd.percent = cgutil_opt.grppct; - cgutil_vaddr[clsgid]->ginfo.cls.rempct -= cgutil_opt.grppct; - - grp->ainfo.shares = MAX_CLASS_CPUSHARES * grp->ginfo.wd.percent / 100; - grp->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, grp->ginfo.wd.percent); - grp->percent = cgutil_vaddr[clsgid]->percent * grp->ginfo.wd.percent / 100; -} - -/* - * function name: cgconf_convert_group - * description : fill the old group inforation into new group - * - */ -void cgconf_convert_group(gscgroup_grp_t* newgrp, gscgroup_old_grp_t* oldgrp) -{ - errno_t sret; - - newgrp->used = oldgrp->used; - newgrp->gid = oldgrp->gid; - - newgrp->gtype = oldgrp->gtype; - - /* set the group internal info */ - newgrp->ginfo.cls.tgid = oldgrp->ginfo.cls.tgid; - newgrp->ginfo.cls.maxlevel = oldgrp->ginfo.cls.maxlevel; - newgrp->ginfo.cls.percent = oldgrp->ginfo.cls.percent; - newgrp->ginfo.cls.rempct = oldgrp->ginfo.cls.rempct; - - sret = strncpy_s(newgrp->grpname, sizeof(newgrp->grpname), oldgrp->grpname, sizeof(oldgrp->grpname) - 1); - securec_check_errno(sret, , ); - - /* set the allocation info */ - newgrp->ainfo.shares = oldgrp->ainfo.shares; - newgrp->ainfo.weight = oldgrp->ainfo.weight; - newgrp->ainfo.quota = oldgrp->ainfo.quota; - - sret = strncpy_s(newgrp->ainfo.iopsread, - sizeof(newgrp->ainfo.iopsread), - oldgrp->ainfo.iopsread, - sizeof(oldgrp->ainfo.iopsread) - 1); - securec_check_errno(sret, , ); - sret = strncpy_s(newgrp->ainfo.iopswrite, - sizeof(newgrp->ainfo.iopswrite), - oldgrp->ainfo.iopswrite, - sizeof(oldgrp->ainfo.iopswrite) - 1); - securec_check_errno(sret, , ); - sret = strncpy_s( - newgrp->ainfo.bpsread, sizeof(newgrp->ainfo.bpsread), oldgrp->ainfo.bpsread, sizeof(oldgrp->ainfo.bpsread) - 1); - securec_check_errno(sret, , ); - sret = strncpy_s(newgrp->ainfo.bpswrite, - sizeof(newgrp->ainfo.bpswrite), - oldgrp->ainfo.bpswrite, - sizeof(oldgrp->ainfo.bpswrite) - 1); - securec_check_errno(sret, , ); - - /* set the exception info */ - for (int i = 0; i < EXCEPT_ALL_KINDS; ++i) { - newgrp->except[i].blocktime = oldgrp->except[i].blocktime; - newgrp->except[i].elapsedtime = oldgrp->except[i].elapsedtime; - newgrp->except[i].allcputime = oldgrp->except[i].allcputime; - newgrp->except[i].qualitime = oldgrp->except[i].qualitime; - newgrp->except[i].skewpercent = oldgrp->except[i].skewpercent; - } - - sret = strncpy_s(newgrp->cpuset, sizeof(newgrp->cpuset), oldgrp->cpuset, sizeof(oldgrp->cpuset) - 1); - securec_check_errno(sret, , ); - - newgrp->percent = (unsigned int)oldgrp->percent; -} - -/* - * function name: cgconf_generate_file_by_root - * description : generate the configuration file by root user - * - * Note: the configuration file must exist in the "etc" directory - */ -int cgconf_generate_file_by_root(long fsize, char* cfgpath) -{ - void* vaddr = NULL; - errno_t sret; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - - /* when deleting the configure file, it must exist! */ - if (cgutil_opt.dflag && (-1 == fsize)) { - fprintf(stderr, "ERROR: the user %s doesn't exist.\n", cgutil_opt.user); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - /* file doesn't exist, create new one */ - if (fsize == -1) { - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); - if (NULL == vaddr) { - fprintf(stderr, "ERROR: failed to create and map the configure file %s!\n", cfgpath); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - sret = memset_s(vaddr, cglen, 0, cglen); - securec_check_errno(sret, free(cfgpath), -1); - - /* rewrite the mapping file */ - cgconf_generate_default_config_file(vaddr); - } else { - fprintf(stderr, "ERROR: the file %s has been corrupted, Please remove it and recreate.\n", cfgpath); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - free(cfgpath); - cfgpath = NULL; - return 0; -} - -/* - * function name: cgconf_generate_file_by_user - * description : generate the configuration file by non-root user - * - * Note: the configuration file must exist in the "etc" directory - */ -int cgconf_generate_file_by_user(long fsize, char* cfgpath) -{ - void* vaddr = NULL; - errno_t sret; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - - if (fsize == -1) { - if ('\0' == cgutil_opt.nodegroup[0]) { - fprintf(stderr, - "ERROR: the configure file %s doesn't exist!\n" - "HINT: please create it by root user!\n", - cfgpath); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - if (cgutil_opt.nodegroup[0] && 0 == cgutil_opt.cflag) { - fprintf(stderr, - "ERROR: the configure file %s doesn't exist!\n" - "HINT: please create it before using it!\n", - cfgpath); - free(cfgpath); - cfgpath = NULL; - return -1; - } else { - /* change origin cluster to virtual cluster */ - if (cgutil_opt.rename) { - int old_cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + - sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + - sizeof(GSCFG_SUFFIX) + 1; - char* old_cfgpath = (char*)malloc(old_cfgpath_len); - if (old_cfgpath == NULL) { - free(cfgpath); - cfgpath = NULL; - return -1; - } - - sret = snprintf_s(old_cfgpath, - old_cfgpath_len, - old_cfgpath_len - 1, - "%s/%s/%s_%s%s", - cgutil_opt.hpath, - GSCGROUP_CONF_DIR, - GSCFG_PREFIX, - cgutil_passwd_user->pw_name, - GSCFG_SUFFIX); - if (sret != EOK) { - fprintf(stderr, "ERROR: failed to construct old cgroup config path"); - free(old_cfgpath); - old_cfgpath = NULL; - free(cfgpath); - cfgpath = NULL; - return -1; - } - - /* rename the old cfgpath to new cfgpath */ - int ret = rename(old_cfgpath, cfgpath); - if (ret != 0) { - fprintf(stderr, "ERROR: failed to rename %s to %s!\n", cfgpath, old_cfgpath); - free(old_cfgpath); - old_cfgpath = NULL; - free(cfgpath); - cfgpath = NULL; - return -1; - } - - /* reset configure path */ - free(cfgpath); - cfgpath = NULL; - cfgpath = old_cfgpath; - } - - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); - if (NULL == vaddr) { - fprintf(stderr, "ERROR: failed to create and map the configure file %s!\n", cfgpath); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - sret = memset_s(vaddr, cglen, 0, cglen); - securec_check_errno(sret, free(cfgpath), -1); - /* rewrite the mapping file */ - cgconf_generate_default_config_file(vaddr); - } - } else { - fprintf(stderr, - "ERROR: the configure file size cannot match the current cgroup!\n" - "HINT: please remove the configure file %s and recreate it!\n", - cfgpath); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - free(cfgpath); - cfgpath = NULL; - return 0; -} - -/* - * function name: cgconf_parse_config_file - * description : parse the configuration file and set the global variable - * - * Note: the configuration file must exist in the "etc" directory - */ -int cgconf_parse_nodegroup_config_file(void) -{ - long fsize = 0; - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - int i = 0; - char* cfgpath = NULL; - size_t cfgpath_len; - errno_t sret; - - if ('\0' == cgutil_opt.nodegroup[0]) { - fprintf(stderr, "ERROR: the nodegroup should be specified!\n"); - return -1; - } - - cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + 1 + - sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; - cfgpath = (char*)malloc(cfgpath_len); - if (cfgpath == NULL) { - return -1; - } - - /* get the etc directory */ - sret = snprintf_s(cfgpath, - cfgpath_len, - cfgpath_len - 1, - "%s/%s/%s.%s_%s%s", - cgutil_opt.hpath, - GSCGROUP_CONF_DIR, - cgutil_opt.nodegroup, - GSCFG_PREFIX, - cgutil_passwd_user->pw_name, - GSCFG_SUFFIX); - securec_check_intval(sret, free(cfgpath), -1); - /* get the configure file */ - fsize = gsutil_filesize(cfgpath); - /* configure file doesn't exist or size is not the same */ - if (-1 == fsize || fsize != (long)cglen) { - fprintf(stderr, - "ERROR: the nodegroup configure file doesn't exist or " - "the size of the nodegroup configure file doesn't match!\n"); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); - if (NULL == vaddr) { - fprintf(stderr, "failed to create and map the configure file %s!\n", cfgpath); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - for (i = 0; i < GSCGROUP_ALLNUM; i++) { - cgutil_vaddr[i] = (gscgroup_grp_t*)vaddr + i; - - if (i == TOPCG_CLASS) { - sret = strcpy_s(cgutil_vaddr[i]->grpname, GPNAME_LEN, cgutil_opt.nodegroup); - securec_check_intval(sret, free(cfgpath), -1); - } - } - - free(cfgpath); - cfgpath = NULL; - return 0; -} - -/* - * function name: cgconf_get_config_path - * description : get the configuration file - * - * Note: the configuration file must exist in the "etc" directory - */ -char* cgconf_get_config_path(bool backup) -{ - char* cfgpath = NULL; - size_t cfgpath_len; - errno_t sret; - - if ('\0' == cgutil_opt.nodegroup[0]) { - if (false == backup) { - cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + sizeof(GSCFG_PREFIX) + 1 + - strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; - } else { - cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + sizeof(GSCFG_PREFIX) + 1 + - strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + sizeof(GSCFG_BACKUP) + 1; - } - } else { - if (false == backup) { - cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + - 1 + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; - } else { - cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + - 1 + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + - sizeof(GSCFG_BACKUP) + 1; - } - } - - cfgpath = (char*)malloc(cfgpath_len); - if (cfgpath == NULL) { - return NULL; - } - - /* get the etc directory */ - if ('\0' == cgutil_opt.nodegroup[0]) { - if (false == backup) { - sret = snprintf_s(cfgpath, - cfgpath_len, - cfgpath_len - 1, - "%s/%s/%s_%s%s", - cgutil_opt.hpath, - GSCGROUP_CONF_DIR, - GSCFG_PREFIX, - cgutil_passwd_user->pw_name, - GSCFG_SUFFIX); - } else { - sret = snprintf_s(cfgpath, - cfgpath_len, - cfgpath_len - 1, - "%s/%s/%s_%s%s%s", - cgutil_opt.hpath, - GSCGROUP_CONF_DIR, - GSCFG_PREFIX, - cgutil_passwd_user->pw_name, - GSCFG_SUFFIX, - GSCFG_BACKUP); - } - } else { - if (false == backup) { - sret = snprintf_s(cfgpath, - cfgpath_len, - cfgpath_len - 1, - "%s/%s/%s.%s_%s%s", - cgutil_opt.hpath, - GSCGROUP_CONF_DIR, - cgutil_opt.nodegroup, - GSCFG_PREFIX, - cgutil_passwd_user->pw_name, - GSCFG_SUFFIX); - } else { - sret = snprintf_s(cfgpath, - cfgpath_len, - cfgpath_len - 1, - "%s/%s/%s.%s_%s%s%s", - cgutil_opt.hpath, - GSCGROUP_CONF_DIR, - cgutil_opt.nodegroup, - GSCFG_PREFIX, - cgutil_passwd_user->pw_name, - GSCFG_SUFFIX, - GSCFG_BACKUP); - } - } - - securec_check_intval(sret, free(cfgpath), NULL); - - return cfgpath; -} - -bool cgconf_gid_invalid(void) -{ - if (cgutil_vaddr[TOPCG_ROOT] == NULL || cgutil_vaddr[TOPCG_GAUSSDB] == NULL || - cgutil_vaddr[TOPCG_BACKEND] == NULL || cgutil_vaddr[BACKENDCG_START_ID] == NULL || - cgutil_vaddr[BACKENDCG_START_ID + 1] == NULL || cgutil_vaddr[TOPCG_CLASS] == NULL || - cgutil_vaddr[CLASSCG_START_ID] == NULL || cgutil_vaddr[WDCG_START_ID] == NULL || - cgutil_vaddr[TSCG_START_ID] == NULL) { - return true; - } - if (cgutil_vaddr[TOPCG_ROOT]->gid != TOPCG_ROOT || cgutil_vaddr[TOPCG_GAUSSDB]->gid != TOPCG_GAUSSDB || - cgutil_vaddr[TOPCG_BACKEND]->gid != TOPCG_BACKEND || - cgutil_vaddr[BACKENDCG_START_ID]->gid != BACKENDCG_START_ID || - cgutil_vaddr[BACKENDCG_START_ID + 1]->gid != (BACKENDCG_START_ID + 1) || - cgutil_vaddr[TOPCG_CLASS]->gid != TOPCG_CLASS || cgutil_vaddr[CLASSCG_START_ID]->gid != CLASSCG_START_ID || - cgutil_vaddr[WDCG_START_ID]->gid != WDCG_START_ID || cgutil_vaddr[TSCG_START_ID]->gid != TSCG_START_ID) { - return true; - } - return false; -} - -/* - * function name: cgconf_parse_config_file - * description : parse the configuration file and set the global variable - * Note: the configuration file must exist in the "etc" directory - */ -int cgconf_parse_config_file(void) -{ - long fsize = 0; - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - int i = 0; - int ret = -1; - char* cfgpath = NULL; - - /* get the configure path */ - cfgpath = cgconf_get_config_path(false); - if (NULL == cfgpath) { - return -1; - } - - fsize = gsutil_filesize(cfgpath); - /* configure file doesn't exist or size is not the same*/ - if (-1 == fsize || fsize != (long)cglen) { - if (geteuid() == 0) { - ret = cgconf_generate_file_by_root(fsize, cfgpath); - } else { - if (cgutil_opt.cflag && *cgutil_opt.nodegroup && *cgutil_opt.clsname == '\0') - ret = cgconf_generate_file_by_user(fsize, cfgpath); - else if (*cgutil_opt.nodegroup) { - free(cfgpath); - cfgpath = NULL; - fprintf(stderr, "ERROR: the specified node group %s doesn't exist!\n", cgutil_opt.nodegroup); - } else { - free(cfgpath); - cfgpath = NULL; - } - } - - if (-1 == ret) { - return -1; /* cfgpath has been freed, no need free here */ - } - } else { - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); - - if (NULL == vaddr) { - fprintf(stderr, "failed to create and map the configure file %s!\n", cfgpath); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - for (i = 0; i < GSCGROUP_ALLNUM; i++) { - cgutil_vaddr[i] = (gscgroup_grp_t*)vaddr + i; - if (cgutil_vaddr[i]->gid >= GSCGROUP_ALLNUM) { - fprintf(stderr, "cgroup gid in configure file %s is out of range !\n", cfgpath); - free(cfgpath); - cfgpath = NULL; - return -1; - } - } - if (cgconf_gid_invalid()) { - fprintf(stderr, "cgroup gid in configure file %s is invalid !\n", cfgpath); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - free(cfgpath); - cfgpath = NULL; - } - return 0; -} - -/* - * function name: cgconf_map_nodegroup_conffile - * description : return the mapping information of original configuration file - * - */ -void* cgconf_map_nodegroup_conffile(void) -{ - long fsize = 0; - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - char* cfgpath = NULL; - size_t cfgpath_len; - errno_t sret; - cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + 1 + - sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; - - cfgpath = (char*)malloc(cfgpath_len); - if (NULL == cfgpath) { - return NULL; - } - - /* get the etc directory */ - sret = snprintf_s(cfgpath, - cfgpath_len, - cfgpath_len - 1, - "%s/%s/%s.%s_%s%s", - cgutil_opt.hpath, - GSCGROUP_CONF_DIR, - cgutil_opt.nodegroup, - GSCFG_PREFIX, - cgutil_passwd_user->pw_name, - GSCFG_SUFFIX); - securec_check_intval(sret, free(cfgpath), NULL); - fsize = gsutil_filesize(cfgpath); - /* make sure that the file exists when recovering */ - if (-1 == fsize) { - free(cfgpath); - cfgpath = NULL; - return NULL; - } - - /* configure file doesn't exist or size is not the same */ - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); - free(cfgpath); - cfgpath = NULL; - - return vaddr; -} - -/* - * function name: cgconf_map_origin_conffile - * description : return the mapping information of original configuration file - */ -void* cgconf_map_origin_conffile(void) -{ - long fsize = 0; - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - char* cfgpath = NULL; - size_t cfgpath_len; - errno_t sret; - - cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + sizeof(GSCFG_PREFIX) + 1 + - strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; - - cfgpath = (char*)malloc(cfgpath_len); - if (NULL == cfgpath) { - return NULL; - } - - sret = snprintf_s(cfgpath, - cfgpath_len, - cfgpath_len - 1, - "%s/%s/%s_%s%s", - cgutil_opt.hpath, - GSCGROUP_CONF_DIR, - GSCFG_PREFIX, - cgutil_passwd_user->pw_name, - GSCFG_SUFFIX); - - securec_check_intval(sret, free(cfgpath), NULL); - fsize = gsutil_filesize(cfgpath); - /* make sure that the file exists when recovering */ - if (-1 == fsize) { - free(cfgpath); - cfgpath = NULL; - return NULL; - } - - /* configure file doesn't exist or size is not the same */ - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); - free(cfgpath); - cfgpath = NULL; - - return vaddr; -} - -/* - * function name: cgconf_map_backup_conffile - * description : return the mapping information of backup file - */ -void* cgconf_map_backup_conffile(bool flag) -{ - long fsize = 0; - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - char* cfgpath = NULL; - - /* get the configure path */ - cfgpath = cgconf_get_config_path(true); - if (NULL == cfgpath) { - return NULL; - } - - fsize = gsutil_filesize(cfgpath); - /* make sure that the file exists when recovering */ - if (flag == false && -1 == fsize) { - free(cfgpath); - cfgpath = NULL; - return NULL; - } - - /* configure file doesn't exist or size is not the same*/ - vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); - free(cfgpath); - cfgpath = NULL; - - return vaddr; -} - -/* - * function name: cgconf_backup_config_file - * description : backup the configuration file when creating/dropping/updating cgroups - * - * Note: the configuration file must exist in the "etc" directory - */ -int cgconf_backup_config_file(void) -{ - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - errno_t sret; - - if (geteuid() == 0) { - return 0; - } - - vaddr = cgconf_map_backup_conffile(true); - if (NULL == vaddr) { - fprintf(stderr, "failed to create and map the backup configure file!\n"); - return -1; - } - - sret = memcpy_s(vaddr, cglen, cgutil_vaddr[0], cglen); - securec_check_errno(sret, (void)munmap(vaddr, cglen);, -1); - - (void)munmap(vaddr, cglen); - - return 0; -} - -/* - * function name: cgconf_remove_backup_conffile - * description : remove the backuping configuration file when creating/dropping/updating cgroups - * - * Note: the configuration file must exist in the "etc" directory - */ -void cgconf_remove_backup_conffile(void) -{ - char* cfgpath = NULL; - - if (geteuid() == 0) { - return; - } - - /* get the configure path */ - cfgpath = cgconf_get_config_path(true); - if (NULL == cfgpath) { - return; - } - - (void)unlink(cfgpath); - - free(cfgpath); - cfgpath = NULL; -} - -#define CGCONFIG_DISPLAY_CPU_QUOTA(cg) \ - { \ - if ((cg) && (cg)->ainfo.quota) { \ - fprintf(stdout, " Quota(%%): %2d", cgutil_vaddr[i]->ainfo.quota); \ - } \ - fprintf(stdout, " Cores: %s", cgutil_vaddr[i]->cpuset); \ - } -/* - * function name: cgconf_display_exception_detail - * description : display the group exception detail information - * - */ -static void cgconf_display_exception_detail(int gid, int kinds) -{ - int i = 0; - - for (i = 0; i < kinds; ++i) { - if (gsutil_exception_kind_is_valid(cgutil_vaddr[gid], i) == 0) - continue; - - if (i == EXCEPT_ABORT) { - fprintf(stdout, "%s: ", gsutil_print_exception_flag(i)); - if (cgutil_vaddr[gid]->except[i].blocktime > 0) - fprintf(stdout, "BlockTime=%u ", cgutil_vaddr[gid]->except[i].blocktime); - if (cgutil_vaddr[gid]->except[i].elapsedtime > 0) - fprintf(stdout, "ElapsedTime=%u ", cgutil_vaddr[gid]->except[i].elapsedtime); - if (cgutil_vaddr[gid]->except[i].spoolsize > 0) - fprintf(stdout, "SpillSize=%ld ", cgutil_vaddr[gid]->except[i].spoolsize); - if (cgutil_vaddr[gid]->except[i].broadcastsize > 0) - fprintf(stdout, "BroadcastSize=%ld ", cgutil_vaddr[gid]->except[i].broadcastsize); - if (cgutil_vaddr[gid]->except[i].allcputime > 0) - fprintf(stdout, "AllCpuTime=%u ", cgutil_vaddr[gid]->except[i].allcputime); - if (cgutil_vaddr[gid]->except[i].qualitime > 0) - fprintf(stdout, "QualificationTime=%u ", cgutil_vaddr[gid]->except[i].qualitime); - if (cgutil_vaddr[gid]->except[i].skewpercent > 0) - fprintf(stdout, "CPUSkewPercent=%u ", cgutil_vaddr[gid]->except[i].skewpercent); - } else { - fprintf(stdout, "%s: ", gsutil_print_exception_flag(i)); - if (cgutil_vaddr[gid]->except[i].allcputime > 0) - fprintf(stdout, "AllCpuTime=%u ", cgutil_vaddr[gid]->except[i].allcputime); - if (cgutil_vaddr[gid]->except[i].qualitime > 0) - fprintf(stdout, "QualificationTime=%u ", cgutil_vaddr[gid]->except[i].qualitime); - if (cgutil_vaddr[gid]->except[i].skewpercent > 0) - fprintf(stdout, "CPUSkewPercent=%u ", cgutil_vaddr[gid]->except[i].skewpercent); - } - - fprintf(stdout, "\n"); - } -} -/* - * function name: cgconf_display_exception - * description : display the group exception information - * - */ -static void cgconf_display_exception(void) -{ - int cls = 0; - int wd = 0; - int flag = 0; - int pflag = 0; - int kinds = EXCEPT_ALL_KINDS; - - fprintf(stdout, "\n\nGroup Exception information is listed:"); - - /* check if the class exists */ - for (cls = CLASSCG_START_ID; cls <= CLASSCG_END_ID; cls++) { - if (cgutil_vaddr[cls]->used == 0) { - continue; - } - - flag = 0; - - if (gsutil_exception_is_valid(cgutil_vaddr[cls], kinds) != 0) { - fprintf(stdout, - "\nGID: %3d Type: %-6s Class: %-16s\n", - cgutil_vaddr[cls]->gid, - "EXCEPTION", - cgutil_vaddr[cls]->grpname); - - cgconf_display_exception_detail(cls, kinds); - - ++flag; - ++pflag; - } - - for (wd = WDCG_START_ID; wd <= WDCG_END_ID; wd++) { - if (cgutil_vaddr[wd]->used == 0 || cgutil_vaddr[wd]->ginfo.wd.cgid != cls || - gsutil_exception_is_valid(cgutil_vaddr[wd], kinds) == 0) - continue; - - if (flag == 0) { - /* display the Class group information */ - fprintf(stdout, - "\nGID: %3d Type: %-6s Class: %-16s", - cgutil_vaddr[cls]->gid, - "EXCEPTION", - cgutil_vaddr[cls]->grpname); - ++flag; - } - - fprintf(stdout, - "\nGID: %3d Type: %-6s Group: %s:%-16s\n", - cgutil_vaddr[wd]->gid, - "EXCEPTION", - cgutil_vaddr[cls]->grpname, - cgutil_vaddr[wd]->grpname); - - cgconf_display_exception_detail(wd, kinds); - - ++pflag; - } - } - - if (pflag == 0) { - fprintf(stdout, "\n"); - } -} - -/* - * function name: cgconf_display_groups - * description : display the configuration file information - * - */ -void cgconf_display_groups(void) -{ - int i; - - /* display the top group information */ - fprintf(stdout, "\nTop Group information is listed:"); - - for (i = 0; i <= TOPCG_END_ID; i++) { - if ('\0' != cgutil_opt.nodegroup[0] && i != TOPCG_CLASS) - continue; - - fprintf(stdout, - "\nGID: %3d Type: %-6s Percent(%%): %4u(%3d) Name: %-20s ", - cgutil_vaddr[i]->gid, - cgconf_get_group_type(cgutil_vaddr[i]->gtype), - cgutil_vaddr[i]->percent, - cgutil_vaddr[i]->ginfo.top.percent, - cgutil_vaddr[i]->grpname); - - CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); - } - - /* display the Backend group information */ - if ('\0' == cgutil_opt.nodegroup[0]) - fprintf(stdout, "\n\nBackend Group information is listed:"); - - for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { - if (0 == cgutil_vaddr[i]->used || '\0' != cgutil_opt.nodegroup[0]) - continue; - - fprintf(stdout, - "\nGID: %3d Type: %-6s Name: %-16s " - "TopGID: %3d Percent(%%): %3u(%2d)", - cgutil_vaddr[i]->gid, - cgconf_get_group_type(cgutil_vaddr[i]->gtype), - cgutil_vaddr[i]->grpname, - cgutil_vaddr[i]->ginfo.cls.tgid, - cgutil_vaddr[i]->percent, - cgutil_vaddr[i]->ginfo.cls.percent); - - CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); - } - - /* display the Class group information */ - fprintf(stdout, "\n\nClass Group information is listed:"); - - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (0 == cgutil_vaddr[i]->used) - continue; - - fprintf(stdout, - "\nGID: %3d Type: %-6s Name: %-16s TopGID: %3d " - "Percent(%%): %3u(%2d) MaxLevel: %d RemPCT: %3d", - cgutil_vaddr[i]->gid, - cgconf_get_group_type(cgutil_vaddr[i]->gtype), - cgutil_vaddr[i]->grpname, - cgutil_vaddr[i]->ginfo.cls.tgid, - cgutil_vaddr[i]->percent, - cgutil_vaddr[i]->ginfo.cls.percent, - cgutil_vaddr[i]->ginfo.cls.maxlevel, - cgutil_vaddr[i]->ginfo.cls.rempct); - - CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); - } - - /* display the Workload group information */ - fprintf(stdout, "\n\nWorkload Group information is listed:"); - - for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { - if (0 == cgutil_vaddr[i]->used || - 0 == strncmp(cgutil_vaddr[i]->grpname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1)) - continue; - - fprintf(stdout, - "\nGID: %3d Type: %-6s Name: %-16s ClsGID: %3d " - "Percent(%%): %3u(%2d) WDLevel: %2d", - cgutil_vaddr[i]->gid, - cgconf_get_group_type(cgutil_vaddr[i]->gtype), - cgutil_vaddr[i]->grpname, - cgutil_vaddr[i]->ginfo.wd.cgid, - cgutil_vaddr[i]->percent, - cgutil_vaddr[i]->ginfo.wd.percent, - cgutil_vaddr[i]->ginfo.wd.wdlevel); - - CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); - } - - /* display the Timeshare group information */ - fprintf(stdout, "\n\nTimeshare Group information is listed:"); - - for (i = TSCG_START_ID; i <= TSCG_END_ID; i++) { - fprintf(stdout, - "\nGID: %3d Type: %-6s Name: %-16s Rate: %d", - cgutil_vaddr[i]->gid, - cgconf_get_group_type(cgutil_vaddr[i]->gtype), - cgutil_vaddr[i]->grpname, - cgutil_vaddr[i]->ginfo.ts.rate); - } - - cgconf_display_exception(); - - fprintf(stdout, "\n"); - (void)fflush(stdout); -} -- 2.34.1 From 704877f632a7f152edba7ccca45d64bb50c0ef68 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 19:59:02 +0800 Subject: [PATCH 02/56] ADD file via upload --- src/bin/gs_cgroup/cgconf.cpp | 1878 ++++++++++++++++++++++++++++++++++ 1 file changed, 1878 insertions(+) create mode 100644 src/bin/gs_cgroup/cgconf.cpp diff --git a/src/bin/gs_cgroup/cgconf.cpp b/src/bin/gs_cgroup/cgconf.cpp new file mode 100644 index 000000000..4c987def5 --- /dev/null +++ b/src/bin/gs_cgroup/cgconf.cpp @@ -0,0 +1,1878 @@ +/* COPYRIGHT (c) 2020华为技术有限公司。 + * + * openGauss基于Mulan PSL v2许可授权规则发行。 + * 您可以使用该软件,具体条件见Mulan PSL v2。 + * 您可以从以下地址获得Mulnan PSL v2的副本: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * 此软件基于"按原样"提供,没有任何形式的保证, + * 无论是明示的还是暗示的,包括但不限于不侵权保证、 + * 适销性或特定用途的隐含保证。 + * 请参阅Mulan PSL v2中的详情。 + * ------------------------------------------------------------------------- + * + * cgconf.cpp + * Cgroup配置文件处理函数 + * + * 标识符 + * src/bin/gs_cgroup/cgconf.cpp + * + * ------------------------------------------------------------------------- + */ +#include +#include +#include +#include /* stat */ +#include +#include /* mmap */ +#include + +#include "securec.h" +#include "cgutil.h" + + /* + ***************** STATIC FUNCTIONS ************************ + */ + + /* + * 函数名称:cgconf_get_group_type + * 函数功能:获取组类型的字符串 + * 参数列表:组类型的枚举值 + * 返回值:组类型的字符串 + */ +char* cgconf_get_group_type(group_type gtype) +{ + if (gtype == GROUP_TOP) + return "Top"; + else if (gtype == GROUP_CLASS) + return "CLASS"; + else if (gtype == GROUP_BAKWD) + return "BAKWD"; + else if (gtype == GROUP_DEFWD) + return "DEFWD"; + else if (gtype == GROUP_TSWD) + return "TSWD"; + + return NULL; +} + +/* + * 函数名称:cgconf_set_root_group + * 函数功能:在配置文件中设置根组的默认值 + * + * 注意:根组不能设置IO相关的权重。 + * 百分比是基于1000来计算的。 + */ +static void cgconf_set_root_group(void) +{ + errno_t sret; + cgutil_vaddr[TOPCG_ROOT]->used = 1; // 标记根组已使用 + cgutil_vaddr[TOPCG_ROOT]->gid = TOPCG_ROOT; // 设置根组的gid为根组标识符TOPCG_ROOT + cgutil_vaddr[TOPCG_ROOT]->gtype = GROUP_TOP; // 设置根组的类型为GROUP_TOP + sret = strncpy_s(cgutil_vaddr[TOPCG_ROOT]->grpname, GPNAME_LEN, GSCGROUP_ROOT, GPNAME_LEN - 1); // 将根组的组名设置为GSCGROUP_ROOT + securec_check_errno(sret, , ); + cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = 100 * DEFAULT_IO_WEIGHT / MAX_IO_WEIGHT; // 设置根组的IO百分比为默认IO权重(DEFAULT_IO_WEIGHT) / 最大IO权重(MAX_IO_WEIGHT) + cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = DEFAULT_IO_WEIGHT; // 设置根组的IO权重为默认权重(DEFAULT_IO_WEIGHT) + cgutil_vaddr[TOPCG_ROOT]->percent = 1000; // 设置根组的百分比为1000 + + /* 将根组设置为默认的CPU集合 */ + sret = snprintf_s(cgutil_vaddr[TOPCG_ROOT]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); + securec_check_intval(sret, , ); +} + +/* + * 函数名称:cgconf_set_gauss_group + * 函数功能:在配置文件中设置Gaussdb组的默认值 + * + * 注意:IO权重的值设置为1000 (MAX_IO_WEIGHT)。 + * 意味着gaussdb可以使用最大的IO资源。 + * 百分比是基于1000来计算的。 + */ +static void cgconf_set_gauss_group(void) +{ + errno_t sret; + cgutil_vaddr[TOPCG_GAUSSDB]->used = 1; // 标记Gaussdb组已使用 + cgutil_vaddr[TOPCG_GAUSSDB]->gid = TOPCG_GAUSSDB; // 设置Gaussdb组的gid为Gaussdb组标识符TOPCG_GAUSSDB + cgutil_vaddr[TOPCG_GAUSSDB]->gtype = GROUP_TOP; // 设置Gaussdb组的类型为GROUP_TOP + sret = strncpy_s(cgutil_vaddr[TOPCG_GAUSSDB]->grpname, GPNAME_LEN, GSCGROUP_GAUSSDB, GPNAME_LEN - 1); // 将Gaussdb组的组名设置为GSCGROUP_GAUSSDB + securec_check_errno(sret, , ); + cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent = 1000; // 设置Gaussdb组的IO百分比为1000 + cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.weight = MAX_IO_WEIGHT; // 设置Gaussdb组的IO权重为最大IO权重(MAX_IO_WEIGHT) + cgutil_vaddr[TOPCG_GAUSSDB]->percent = 1000; // 设置Gaussdb组的百分比为1000 + + /* 将Gaussdb组设置为默认的CPU集合 */ + sret = snprintf_s(cgutil_vaddr[TOPCG_GAUSSDB]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); + securec_check_intval(sret, , ); +} +/* + * 函数名:cgconf_set_top_class_group + * 功能:设置默认的顶级分类组的值 + * + */ +static void cgconf_set_top_class_group(void) +{ + errno_t sret; + // 设置顶级分类组的used为1,表示已经被使用 + cgutil_vaddr[TOPCG_CLASS]->used = 1; + // 设置顶级分类组的gid为TOPCG_CLASS + cgutil_vaddr[TOPCG_CLASS]->gid = TOPCG_CLASS; + // 设置顶级分类组的gtype为GROUP_TOP + cgutil_vaddr[TOPCG_CLASS]->gtype = GROUP_TOP; + // 将GSCGROUP_TOP_CLASS拷贝到顶级分类组的grpname中 + sret = strncpy_s(cgutil_vaddr[TOPCG_CLASS]->grpname, GPNAME_LEN, GSCGROUP_TOP_CLASS, GPNAME_LEN - 1); + securec_check_errno(sret, , ); + // 设置顶级分类组的ginfo.top.percent为TOP_CLASS_PERCENT + cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = TOP_CLASS_PERCENT; + // 设置顶级分类组的ainfo.shares为DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10 + cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10; + // 设置顶级分类组的ainfo.weight为IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT) + cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT); + // 设置顶级分类组的percent为cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100 + cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100; + // 如果顶级分类组的cpuset为空,则将cgutil_vaddr[TOPCG_GAUSSDB]->cpuset拷贝到顶级分类组的cpuset中 + if (*cgutil_vaddr[TOPCG_CLASS]->cpuset == '\0') { + sret = snprintf_s( + cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); + securec_check_intval(sret, , ); + } +} + +/* + * 函数名:cgconf_set_nodegroup_top_group + * 功能:设置默认的节点组顶级组的值 + * + */ +static void cgconf_set_nodegroup_top_group(void) +{ + errno_t sret; + // 设置顶级分类组的used为1,表示已经被使用 + cgutil_vaddr[TOPCG_CLASS]->used = 1; + // 设置顶级分类组的gid为TOPCG_CLASS + cgutil_vaddr[TOPCG_CLASS]->gid = TOPCG_CLASS; + // 设置顶级分类组的gtype为GROUP_TOP + cgutil_vaddr[TOPCG_CLASS]->gtype = GROUP_TOP; + // 将cgutil_opt.nodegroup拷贝到顶级分类组的grpname中 + sret = strncpy_s(cgutil_vaddr[TOPCG_CLASS]->grpname, GPNAME_LEN, cgutil_opt.nodegroup, GPNAME_LEN - 1); + securec_check_errno(sret, , ); + // 设置顶级分类组的ginfo.top.percent为TOP_CLASS_PERCENT + cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = TOP_CLASS_PERCENT; + // 设置顶级分类组的ainfo.shares为DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10 + cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10; + // 设置顶级分类组的ainfo.weight为IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT) + cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT); + // 设置顶级分类组的percent为cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100 + cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100; + // 如果顶级分类组的cpuset为空,则将cgutil_vaddr[TOPCG_GAUSSDB]->cpuset拷贝到顶级分类组的cpuset中 + if (*cgutil_vaddr[TOPCG_CLASS]->cpuset == '\0') { + sret = snprintf_s( + cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); + securec_check_intval(sret, , ); + } +} + +/* + * 函数名:cgconf_set_default_backend_group + * 功能:设置默认的默认后端组的值 + * + */ +void cgconf_set_default_backend_group(void) +{ + errno_t sret; + // 设置后端组的used为1,表示已经被使用 + cgutil_vaddr[BACKENDCG_START_ID]->used = 1; + // 设置后端组的gid为BACKENDCG_START_ID + cgutil_vaddr[BACKENDCG_START_ID]->gid = BACKENDCG_START_ID; + // 设置后端组的gtype为GROUP_BAKWD + cgutil_vaddr[BACKENDCG_START_ID]->gtype = GROUP_BAKWD; + // 设置后端组的ginfo.cls.tgid为TOPCG_BACKEND + cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.tgid = TOPCG_BACKEND; + // 设置后端组的ginfo.cls.percent为DEFAULT_BACKEND_PERCENT + cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.percent = DEFAULT_BACKEND_PERCENT; + // 将GSCGROUP_DEFAULT_BACKEND拷贝到后端组的grpname中 + sret = strncpy_s( + cgutil_vaddr[BACKENDCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_BACKEND, GPNAME_LEN - 1); + securec_check_errno(sret, , ); + // 设置后端组的ainfo.shares为DEFAULT_CPU_SHARES * DEFAULT_BACKEND_PERCENT / 10 + cgutil_vaddr[BACKENDCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_BACKEND_PERCENT / 10; + // 设置后端组的ainfo.weight为IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_BACKEND_PERCENT) + cgutil_vaddr[BACKENDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_BACKEND_PERCENT); + // 设置后端组的percent为cgutil_vaddr[TOPCG_BACKEND]->percent * DEFAULT_BACKEND_PERCENT / 100 + cgutil_vaddr[BACKENDCG_START_ID]->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * DEFAULT_BACKEND_PERCENT / 100; + + // 如果后端组的cpuset为空,则将cgutil_vaddr[TOPCG_BACKEND]->cpuset拷贝到后端组的cpuset中 + if (*cgutil_vaddr[BACKENDCG_START_ID]->cpuset == '\0') { + sret = snprintf_s(cgutil_vaddr[BACKENDCG_START_ID]->cpuset, + CPUSET_LEN, + CPUSET_LEN - 1, + "%s", + cgutil_vaddr[TOPCG_BACKEND]->cpuset); + securec_check_intval(sret, , ); + } +} + +/* + * 函数名:cgconf_set_vacuum_group + * 功能:设置默认的vacuum后端组的值 + * + */ +void cgconf_set_vacuum_group(void) +{ + errno_t sret; + // 设置后端组的used为1,表示已经被使用 + cgutil_vaddr[BACKENDCG_START_ID + 1]->used = 1; + // 设置后端组的gid为BACKENDCG_START_ID + 1 + cgutil_vaddr[BACKENDCG_START_ID + 1]->gid = BACKENDCG_START_ID + 1; + // 设置后端组的gtype为GROUP_BAKWD + cgutil_vaddr[BACKENDCG_START_ID + 1]->gtype = GROUP_BAKWD; + // 设置后端组的ginfo.cls.tgid为TOPCG_BACKEND + cgutil_vaddr[BACKENDCG_START_ID + 1]->ginfo.cls.tgid = TOPCG_BACKEND; + // 设置后端组的ginfo.cls.percent为VACUUM_PERCENT + cgutil_vaddr[BACKENDCG_START_ID + 1]->ginfo.cls.percent = VACUUM_PERCENT; + // 将GSCGROUP_VACUUM拷贝到后端组的grpname中 + sret = + strncpy_s(cgutil_vaddr[BACKENDCG_START_ID + 1]->grpname, GPNAME_LEN, GSCGROUP_VACUUM, GPNAME_LEN - 1); + securec_check_errno(sret, , ); + // 设置后端组的ainfo.shares为DEFAULT_CPU_SHARES * VACUUM_PERCENT / 10 + cgutil_vaddr[BACKENDCG_START_ID + 1]->ainfo.shares = DEFAULT_CPU_SHARES * VACUUM_PERCENT / 10; + // 设置后端组的ainfo.weight为IO_WEIGHT_CALC(MAX_IO_WEIGHT, VACUUM_PERCENT) + cgutil_vaddr[BACKENDCG_START_ID + 1]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, VACUUM_PERCENT); + // 设置后端组的percent为cgutil_vaddr[TOPCG_BACKEND]->percent * VACUUM_PERCENT / 100 + cgutil_vaddr[BACKENDCG_START_ID + 1]->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * VACUUM_PERCENT / 100; + // 如果后端组的cpuset为空,则将cgutil_vaddr[TOPCG_BACKEND]->cpuset拷贝到后端组的cpuset中 + if (*cgutil_vaddr[BACKENDCG_START_ID + 1]->cpuset == '\0') { + sret = snprintf_s(cgutil_vaddr[BACKENDCG_START_ID + 1]->cpuset, + CPUSET_LEN, + CPUSET_LEN - 1, + "%s", + cgutil_vaddr[TOPCG_BACKEND]->cpuset); + securec_check_intval(sret, , ); + } +} +/* + * 函数名称:cgconf_set_default_class_group + * 描述:设置默认的默认类组的值 + * + */ +void cgconf_set_default_class_group(void) +{ + errno_t sret; + + // 设置默认类组的属性值 + cgutil_vaddr[CLASSCG_START_ID]->used = 1; // 是否已使用,默认为1 + cgutil_vaddr[CLASSCG_START_ID]->gid = CLASSCG_START_ID; // 类组的唯一ID,默认为CLASSCG_START_ID + cgutil_vaddr[CLASSCG_START_ID]->gtype = GROUP_CLASS; // 组的类型,默认为GROUP_CLASS + cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.tgid = TOPCG_CLASS; // 类组所属的顶级组,默认为TOPCG_CLASS + cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.maxlevel = 1; // 组的最大层级,默认为1 + cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.percent = DEFAULT_CLASS_PERCENT; // 类组的百分比,默认为DEFAULT_CLASS_PERCENT + cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.rempct = 100; // 剩余的百分比,默认为100 + sret = strncpy_s(cgutil_vaddr[CLASSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_CLASS, GPNAME_LEN - 1); // 类组的名称,默认为GSCGROUP_DEFAULT_CLASS + securec_check_errno(sret, , ); + cgutil_vaddr[CLASSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_CLASS_PERCENT / 10; // CPU份额,默认为DEFAULT_CPU_SHARES * DEFAULT_CLASS_PERCENT / 10 + cgutil_vaddr[CLASSCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_CLASS_PERCENT); // IO权重,默认为IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_CLASS_PERCENT) + cgutil_vaddr[CLASSCG_START_ID]->percent = cgutil_vaddr[TOPCG_CLASS]->percent; // 类组的百分比,默认为cgutil_vaddr[TOPCG_CLASS]->percent + + cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].skewpercent = DEFAULT_CPUSKEWPCT; // 异常情况下的偏斜百分比,默认为DEFAULT_CPUSKEWPCT + cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].qualitime = DEFAULT_QUALITIME; // 异常等待时间,默认为DEFAULT_QUALITIME +} + +/* + * 函数名称:cgconf_set_default_top_workload_group + * 描述:设置默认的顶级工作负载组的值 + * + */ +static void cgconf_set_default_top_workload_group(void) +{ + char tmpstr[GPNAME_LEN]; + errno_t sret; + + // 设置默认顶级工作负载组的属性值 + cgutil_vaddr[WDCG_START_ID]->used = 1; // 是否已使用,默认为1 + cgutil_vaddr[WDCG_START_ID]->gid = WDCG_START_ID; // 工作负载组的唯一ID,默认为WDCG_START_ID + cgutil_vaddr[WDCG_START_ID]->gtype = GROUP_DEFWD; // 组的类型,默认为GROUP_DEFWD + cgutil_vaddr[WDCG_START_ID]->ginfo.wd.cgid = CLASSCG_START_ID; // 工作负载组所属的类组,默认为CLASSCG_START_ID + cgutil_vaddr[WDCG_START_ID]->ginfo.wd.wdlevel = 1; // 工作负载组的级别,默认为1 + sret = snprintf_s(tmpstr, sizeof(tmpstr), sizeof(tmpstr) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); // 构造工作负载组的名称,默认为GSCGROUP_TOP_WORKLOAD:1 + securec_check_intval(sret, , ); + + sret = strncpy_s(cgutil_vaddr[WDCG_START_ID]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); // 工作负载组的名称,默认为tmpstr + securec_check_errno(sret, , ); + + cgutil_vaddr[WDCG_START_ID]->ainfo.shares = MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100; // CPU份额,默认为MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100 + cgutil_vaddr[WDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT); // IO权重,默认为IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT) +} +/* + * function name: cgconf_set_default_timeshare_group + * 功能:设置默认的时间共享组的默认值 + * + */ + //该段代码定义了一个名为cgconf_set_default_timeshare_group的函数,用于设置默认的时间共享组的默认值。 + //函数中使用了cgutil_vaddr[TSCG_START_ID]到cgutil_vaddr[TSCG_START_ID + 3]的数组元素,这是一个全局变量数组,数组的元素类型是一个结构体指针。该结构体用于保存时间共享组的信息。 + //函数首先设置了低级别的默认组,然后设置中级别、高级别和紧急级别的默认组。对于每个组,都设置了使用标志、组ID、组类型、控制组ID、时间共享比例、组名、CPU分享和IO权重。 + //例如,可以通过调用cgconf_set_default_timeshare_group函数来设置默认的时间共享组的默认值,并将该函数应用于操作系统进程调度的相关设置中。 +static void cgconf_set_default_timeshare_group(void) +{ + errno_t sret; + /* 低级别的默认组 */ + cgutil_vaddr[TSCG_START_ID]->used = 1; // 设置使用标志为1,表示该组正在使用 + cgutil_vaddr[TSCG_START_ID]->gid = TSCG_START_ID; // 设置组ID + cgutil_vaddr[TSCG_START_ID]->gtype = GROUP_TSWD; // 设置组类型为时间共享 + cgutil_vaddr[TSCG_START_ID]->ginfo.ts.cgid = CLASSCG_START_ID; // 设置控制组ID + cgutil_vaddr[TSCG_START_ID]->ginfo.ts.rate = TS_LOW_RATE; // 设置时间共享的比例 + sret = strncpy_s(cgutil_vaddr[TSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_LOW_TIMESHARE, GPNAME_LEN - 1); // 将组名拷贝到指定的变量中 + securec_check_errno(sret, , ); // 错误检查 + cgutil_vaddr[TSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * TS_LOW_RATE; // 设置CPU分享 + cgutil_vaddr[TSCG_START_ID]->ainfo.weight = MIN_IO_WEIGHT * TS_LOW_RATE; // 设置IO权重 + + /* 中级别的默认组 */ + cgutil_vaddr[TSCG_START_ID + 1]->used = 1; + cgutil_vaddr[TSCG_START_ID + 1]->gid = TSCG_START_ID + 1; + cgutil_vaddr[TSCG_START_ID + 1]->gtype = GROUP_TSWD; + cgutil_vaddr[TSCG_START_ID + 1]->ginfo.ts.cgid = CLASSCG_START_ID; + cgutil_vaddr[TSCG_START_ID + 1]->ginfo.ts.rate = TS_MEDIUM_RATE; + sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 1]->grpname, GPNAME_LEN, GSCGROUP_MEDIUM_TIMESHARE, GPNAME_LEN - 1); + securec_check_errno(sret, , ); + cgutil_vaddr[TSCG_START_ID + 1]->ainfo.shares = DEFAULT_CPU_SHARES * TS_MEDIUM_RATE; + cgutil_vaddr[TSCG_START_ID + 1]->ainfo.weight = MIN_IO_WEIGHT * TS_MEDIUM_RATE; + + /* 高级别的默认组 */ + cgutil_vaddr[TSCG_START_ID + 2]->used = 1; + cgutil_vaddr[TSCG_START_ID + 2]->gid = TSCG_START_ID + 2; + cgutil_vaddr[TSCG_START_ID + 2]->gtype = GROUP_TSWD; + cgutil_vaddr[TSCG_START_ID + 2]->ginfo.ts.cgid = CLASSCG_START_ID; + cgutil_vaddr[TSCG_START_ID + 2]->ginfo.ts.rate = TS_HIGH_RATE; + sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 2]->grpname, GPNAME_LEN, GSCGROUP_HIGH_TIMESHARE, GPNAME_LEN - 1); + securec_check_errno(sret, , ); + cgutil_vaddr[TSCG_START_ID + 2]->ainfo.shares = DEFAULT_CPU_SHARES * TS_HIGH_RATE; + cgutil_vaddr[TSCG_START_ID + 2]->ainfo.weight = MIN_IO_WEIGHT * TS_HIGH_RATE; + + /* 紧急级别的默认组 */ + cgutil_vaddr[TSCG_START_ID + 3]->used = 1; + cgutil_vaddr[TSCG_START_ID + 3]->gid = TSCG_START_ID + 3; + cgutil_vaddr[TSCG_START_ID + 3]->gtype = GROUP_TSWD; + cgutil_vaddr[TSCG_START_ID + 3]->ginfo.ts.cgid = CLASSCG_START_ID; + cgutil_vaddr[TSCG_START_ID + 3]->ginfo.ts.rate = TS_RUSH_RATE; + sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 3]->grpname, GPNAME_LEN, GSCGROUP_RUSH_TIMESHARE, GPNAME_LEN - 1); + securec_check_errno(sret, , ); + cgutil_vaddr[TSCG_START_ID + 3]->ainfo.shares = DEFAULT_CPU_SHARES * TS_RUSH_RATE; + cgutil_vaddr[TSCG_START_ID + 3]->ainfo.weight = MIN_IO_WEIGHT * TS_RUSH_RATE; +} + +/** + * @Description: 重置cgroup配置。 + * @Return: void + * @See also: + */ +void cgconf_reset_cgroup_config(void) +{ + /* 创建默认的顶层组 */ + cgconf_set_root_group(); + /* 创建gauss组 */ + cgconf_set_gauss_group(); + /* 创建顶层后台组 */ + cgconf_set_top_backend_group(); + + /* 在顶层后台组下创建默认的vacuum组 */ + cgconf_set_default_backend_group(); + cgconf_set_vacuum_group(); + + if (cgutil_opt.nodegroup[0] == '\0' || cgutil_opt.rename) { + /* 创建顶层类组 */ + cgconf_set_top_class_group(); + } + else { + /* 创建节点组顶层组 */ + cgconf_set_nodegroup_top_group(); + } + + /* 在顶层类组下创建默认的类组 */ + cgconf_set_default_class_group(); + cgconf_set_default_top_workload_group(); + + /* 在默认的类组下创建top/rush/high/medium/low timeshare组 */ + cgconf_set_default_timeshare_group(); +} + +/** + * @Description: 恢复io配置。 + * @IN iovalue: 要恢复的io值 + * @Return: void + * @See also: + */ +void cgconf_revert_blkio_value(char* iovalue) +{ + char* p = NULL; + char* q = NULL; + char* head = NULL; + char* i = NULL; + errno_t sret; + + if ((head = strdup(iovalue)) == NULL) { + fprintf(stderr, "revert blkio failed, cannot alloc memory."); + return; + } + + sret = memset_s(iovalue, IODATA_LEN, 0, IODATA_LEN); + securec_check_errno(sret, free(head), ); + + p = head; + do { + q = strchr(p, '\n'); + if (q != NULL) { + *q++ = '\0'; + } + i = p; + while (*i++) { + if (*i == '\t') { + *i = ' '; + break; + } + } + i++; + /* 将设备的blkio限制值(iopsread/iopswrite/bpsread/bpswrite)设置为0,表示对该设备进行恢复 */ + *i = '0'; + + while (*i++) { + *i = '\0'; + } + if (iovalue[0]) { + sret = sprintf_s(iovalue + strlen(iovalue), IODATA_LEN - strlen(iovalue), "\n%s", p); + securec_check_intval(sret, free(head), ); + } + else { + sret = sprintf_s(iovalue, IODATA_LEN, "%s", p); + securec_check_intval(sret, free(head), ); + } + p = q; + } while (q != NULL); + free(head); + head = NULL; +} + +/** + * @Description: 恢复配置文件。 + * @IN void + * @Return: void + * @See also: + */ +void cgconf_revert_config_file(void) +{ + int i = 0; + + /* 获取当前用户名 */ + errno_t sret = snprintf_s( + cgutil_opt.user, sizeof(cgutil_opt.user), sizeof(cgutil_opt.user) - 1, "%s", cgutil_passwd_user->pw_name); + securec_check_intval(sret, , ); + + for (i = 1; i < GSCGROUP_ALLNUM; ++i) { + cgutil_vaddr[i]->used = 0; + *cgutil_vaddr[i]->cpuset = '\0'; + cgutil_vaddr[i]->ainfo.quota = 0; + if (cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) { + if (cgutil_vaddr[i]->ainfo.iopsread[0]) { + cgconf_revert_blkio_value(cgutil_vaddr[i]->ainfo.iopsread); + } + + if (cgutil_vaddr[i]->ainfo.iopswrite[0]) { + cgconf_revert_blkio_value(cgutil_vaddr[i]->ainfo.iopswrite); + } + + if (cgutil_vaddr[i]->ainfo.bpsread[0]) { + cgconf_revert_blkio_value(cgutil_vaddr[i]->ainfo.bpsread); + } + + if (cgutil_vaddr[i]->ainfo.bpswrite[0]) { + cgconf_revert_blkio_value(cgutil_vaddr[i]->ainfo.bpswrite); + } + } + } + + /* 重置配置 */ + cgconf_reset_cgroup_config(); +} +/* + * 函数名称:cgconf_generate_default_config_file + * 描述:生成默认的配置文件 + * + */ +void cgconf_generate_default_config_file(void* vaddr) +{ + int i = 0; + + // 将虚拟地址转换为gscgroup_grp_t类型的指针,并将其赋给cgutil_vaddr数组的相应元素 + for (i = 0; i < GSCGROUP_ALLNUM; i++) { + cgutil_vaddr[i] = (gscgroup_grp_t*)vaddr + i; + // 将used字段重置为0 + cgutil_vaddr[i]->used = 0; + } + + /* 重置配置 */ + // 重置cgroup的配置 + cgconf_reset_cgroup_config(); +} + +/* + * 函数名称:cgconf_update_backend_percent + * 描述:更新后端组的百分比值 + * 注意:此函数在更新后端组的值之后调用 + */ +void cgconf_update_backend_percent(void) +{ + int i; + int percent = 0; + + // 获取所有后端组的使用百分比 + for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { + if (cgutil_vaddr[i]->used) { + percent += cgutil_vaddr[i]->ginfo.cls.percent; + } + } + + // 更新每个后端组的百分比 + for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { + if (cgutil_vaddr[i]->used) { + if (percent == 0) { + fprintf(stderr, "ERROR: the percentage value of backend group is zero.\n"); + continue; + } + cgutil_vaddr[i]->percent = + cgutil_vaddr[TOPCG_BACKEND]->percent * cgutil_vaddr[i]->ginfo.cls.percent / percent; + + } + } +} + +/* + * 函数名称:cgconf_update_backend_group + * 描述:重置指定组的百分比并计算CPU共享和IO权重 + * 参数:后端组的数据结构 + * + * 注意:此函数在更新后端组的值之后调用 + */ +void cgconf_update_backend_group(gscgroup_grp_t* grp) +{ + grp->ginfo.cls.percent = cgutil_opt.bkdpct; + + // 计算CPU共享 + grp->ainfo.shares = DEFAULT_CPU_SHARES * grp->ginfo.cls.percent / 10; + + // 计算IO权重 + grp->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, grp->ginfo.cls.percent); + + // 更新组的百分比 + grp->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * grp->ginfo.cls.percent / 100; +} +/* + * 函数名称:cgconf_update_class_percent + * 描述:更新所有类组和其所有工作负载组的百分比值 + * + * 注意:此函数在更新类组的值之后调用 + */ +void cgconf_update_class_percent(void) +{ + int i; + int j; + int percent = 0; + + // 获取所有类组的总百分比 + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + if (cgutil_vaddr[i]->used) + percent += cgutil_vaddr[i]->ginfo.cls.percent; + } + + // 更新类和工作负载组的百分比 + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + if (cgutil_vaddr[i]->used) { + if (percent == 0) { + continue; + } + cgutil_vaddr[i]->percent = + cgutil_vaddr[TOPCG_CLASS]->percent * cgutil_vaddr[i]->ginfo.cls.percent / percent; + + if (cgutil_vaddr[i]->percent == 0) + cgutil_vaddr[i]->percent = 1; + } + + for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { + if (cgutil_vaddr[j]->used == 0 || cgutil_vaddr[j]->ginfo.wd.cgid != cgutil_vaddr[i]->gid) + continue; + + cgutil_vaddr[j]->percent = cgutil_vaddr[i]->percent * cgutil_vaddr[j]->ginfo.wd.percent / 100; + + if (cgutil_vaddr[j]->percent == 0) + cgutil_vaddr[j]->percent = 1; + } + } +} + +/* + * 函数名称:cgconf_update_top_percent + * 描述:更新所有后端组和类组的百分比值 + * + * 注意:此函数在更新Gaussdb组的值之后调用 + */ +void cgconf_update_top_percent(void) +{ + // 更新后端组的百分比 + cgutil_vaddr[TOPCG_BACKEND]->percent = + cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent / 100; + cgconf_update_backend_percent(); + + // 更新类组的百分比 + cgutil_vaddr[TOPCG_CLASS]->percent = + cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent / 100; + cgconf_update_class_percent(); +} + +/* + * 函数名称:cgconf_set_class_group + * 描述:在创建新的类组时设置类组的相关字段 + * + * 注意:此函数在创建新的类组之后调用 + */ +void cgconf_set_class_group(int gid) +{ + errno_t sret; + cgutil_vaddr[gid]->used = 1; + cgutil_vaddr[gid]->gid = gid; + cgutil_vaddr[gid]->gtype = GROUP_CLASS; + cgutil_vaddr[gid]->ginfo.cls.tgid = TOPCG_CLASS; + cgutil_vaddr[gid]->ginfo.cls.maxlevel = 0; + + cgutil_vaddr[gid]->ginfo.cls.percent = cgutil_opt.clspct; + cgutil_vaddr[gid]->ginfo.cls.rempct = 100; + + // 将clsname拷贝到grpname字段 + sret = strncpy_s(cgutil_vaddr[gid]->grpname, GPNAME_LEN, cgutil_opt.clsname, GPNAME_LEN - 1); + securec_check_errno(sret, , ); + + // 计算CPU共享 + cgutil_vaddr[gid]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_vaddr[gid]->ginfo.cls.percent / 10; + + // 计算IO权重 + cgutil_vaddr[gid]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_vaddr[gid]->ginfo.cls.percent); + + // 将TOPCG_CLASS的cpuset拷贝到cpuset字段 + sret = snprintf_s(cgutil_vaddr[gid]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_CLASS]->cpuset); + securec_check_intval(sret, , ); + + // 更新类组和工作负载组的百分比 + cgconf_update_class_percent(); +} +/* + * function name: cgconf_reset_class_group + * description : 重置类群组字段,当删除一个类群组时调用 + * + * Note: 该函数在删除类群组后调用 + */ +void cgconf_reset_class_group(int gid) +{ + int i; + errno_t sret; + + for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0) + continue; + + // 如果cgutil_vaddr[i]表示的类群组使用且其cgid等于参数gid,则将该类群组的字段清零 + if (cgutil_vaddr[i]->ginfo.wd.cgid == gid) { + sret = memset_s(cgutil_vaddr[i], sizeof(gscgroup_grp_t), 0, sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , ); + } + } + + // 将参数gid表示的类群组的字段清零 + sret = memset_s(cgutil_vaddr[gid], sizeof(gscgroup_grp_t), 0, sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , ); + + // 更新类群组的百分比 + cgconf_update_class_percent(); +} + +/* + * function name: cgconf_update_class_group + * description : 更新类群组的值 + * + * Note: 该函数在更新类群组后调用 + */ +void cgconf_update_class_group(gscgroup_grp_t* grp) +{ + // 更新类群组的百分比 + grp->ginfo.cls.percent = cgutil_opt.clspct; + + // 计算类群组的CPU共享值 + grp->ainfo.shares = DEFAULT_CPU_SHARES * grp->ginfo.cls.percent / 10; + + // 计算类群组的IO权重值 + grp->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, grp->ginfo.cls.percent); + + // 更新类群组的百分比 + cgconf_update_class_percent(); +} + +/* + * function name: cgconf_set_top_workload_group + * description : 设置顶级工作负载组的默认值 + * + */ +void cgconf_set_top_workload_group(int wdgid, int clsgid) +{ + char tmpstr[GPNAME_LEN]; + errno_t sret; + + // 设置顶级工作负载组的相关字段值 + cgutil_vaddr[wdgid]->used = 1; + cgutil_vaddr[wdgid]->gid = wdgid; + cgutil_vaddr[wdgid]->gtype = GROUP_DEFWD; + cgutil_vaddr[wdgid]->ginfo.wd.cgid = clsgid; + cgutil_vaddr[wdgid]->ginfo.wd.wdlevel = 1; + cgutil_vaddr[wdgid]->ginfo.wd.percent = TOPWD_PERCENT; + sret = snprintf_s(tmpstr, sizeof(tmpstr), sizeof(tmpstr) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); + securec_check_intval(sret, , ); + + sret = strncpy_s(cgutil_vaddr[wdgid]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); + securec_check_errno(sret, , ); + + // 计算顶级工作负载组的CPU共享值和IO权重值 + cgutil_vaddr[wdgid]->ainfo.shares = MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100; + cgutil_vaddr[wdgid]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT); + cgutil_vaddr[wdgid]->percent = cgutil_vaddr[clsgid]->percent * cgutil_vaddr[wdgid]->ginfo.wd.percent / 100; + /*在配置文件中设置它的cpuset */ + sret = snprintf_s(cgutil_vaddr[wdgid]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[clsgid]->cpuset); + securec_check_intval(sret, , ); +} +/* + * function name: cgconf_set_workload_group + * description : 创建新的工作负载组时设置工作负载组字段 + * + * Note: 该函数在创建新的工作负载组后调用 + */ +void cgconf_set_workload_group(int wdgid, int clsgid) +{ + char tmpstr[GPNAME_LEN]; + errno_t sret; + + // 设置工作负载组的相关字段值 + cgutil_vaddr[wdgid]->used = 1; + cgutil_vaddr[wdgid]->gid = wdgid; + cgutil_vaddr[wdgid]->gtype = GROUP_DEFWD; + cgutil_vaddr[wdgid]->ginfo.wd.cgid = clsgid; + cgutil_vaddr[wdgid]->ginfo.wd.wdlevel = cgutil_vaddr[clsgid]->ginfo.cls.maxlevel + 1; + cgutil_vaddr[wdgid]->ginfo.wd.percent = cgutil_opt.grppct; + cgutil_vaddr[clsgid]->ginfo.cls.rempct -= cgutil_opt.grppct; + sret = snprintf_s( + tmpstr, sizeof(tmpstr), sizeof(tmpstr) - 1, "%s:%d", cgutil_opt.wdname, cgutil_vaddr[wdgid]->ginfo.wd.wdlevel); + securec_check_intval(sret, , ); + sret = strncpy_s(cgutil_vaddr[wdgid]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); + securec_check_intval(sret, , ); + + // 计算工作负载组的CPU共享值和IO权重值 + cgutil_vaddr[wdgid]->ainfo.shares = MAX_CLASS_CPUSHARES * cgutil_vaddr[wdgid]->ginfo.wd.percent / 100; + cgutil_vaddr[wdgid]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_vaddr[wdgid]->ginfo.wd.percent); + cgutil_vaddr[wdgid]->percent = cgutil_vaddr[clsgid]->percent * cgutil_vaddr[wdgid]->ginfo.wd.percent / 100; + + // 在配置文件中设置工作负载组的cpuset + sret = snprintf_s(cgutil_vaddr[wdgid]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[clsgid]->cpuset); + securec_check_intval(sret, , ); +} + +/* + * function name: cgconf_reset_workload_group + * description : 删除工作负载组时重置工作负载组字段 + * + * Note: 该函数在删除工作负载组后调用 + */ +void cgconf_reset_workload_group(int wdgid) +{ + errno_t sret; + // 将工作负载组的字段清零 + sret = memset_s(cgutil_vaddr[wdgid], sizeof(gscgroup_grp_t), 0, sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , ); +} + +/* + * function name: cgconf_update_workload_group + * description : 更新工作负载组的值 + * + * Note: 该函数在更新工作负载组后调用 + */ +void cgconf_update_workload_group(gscgroup_grp_t* grp) +{ + int clsgid = grp->ginfo.wd.cgid; + // 更新类群组的剩余百分比 + cgutil_vaddr[clsgid]->ginfo.cls.rempct += grp->ginfo.wd.percent; + grp->ginfo.wd.percent = cgutil_opt.grppct; + cgutil_vaddr[clsgid]->ginfo.cls.rempct -= cgutil_opt.grppct; + + // 计算工作负载组的CPU共享值和IO权重值 + grp->ainfo.shares = MAX_CLASS_CPUSHARES * grp->ginfo.wd.percent / 100; + grp->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, grp->ginfo.wd.percent); + grp->percent = cgutil_vaddr[clsgid]->percent * grp->ginfo.wd.percent / 100; +} + +/* + * 函数名称:cgconf_convert_group + * 功能描述:将旧的组信息填充到新的组中 + * + * 参数: + * newgrp: 新的组结构体指针 + * oldgrp: 旧的组结构体指针 + * + * 相似应用实例:当需要将旧的组信息迁移到新的系统中时,可以使用该函数。 + */ +void cgconf_convert_group(gscgroup_grp_t* newgrp, gscgroup_old_grp_t* oldgrp) +{ + errno_t sret; + + newgrp->used = oldgrp->used; + newgrp->gid = oldgrp->gid; + + newgrp->gtype = oldgrp->gtype; + + /* 设置组的内部信息 */ + newgrp->ginfo.cls.tgid = oldgrp->ginfo.cls.tgid; + newgrp->ginfo.cls.maxlevel = oldgrp->ginfo.cls.maxlevel; + newgrp->ginfo.cls.percent = oldgrp->ginfo.cls.percent; + newgrp->ginfo.cls.rempct = oldgrp->ginfo.cls.rempct; + + sret = strncpy_s(newgrp->grpname, sizeof(newgrp->grpname), oldgrp->grpname, sizeof(oldgrp->grpname) - 1); + securec_check_errno(sret, , ); + + /* 设置分配信息 */ + newgrp->ainfo.shares = oldgrp->ainfo.shares; + newgrp->ainfo.weight = oldgrp->ainfo.weight; + newgrp->ainfo.quota = oldgrp->ainfo.quota; + + sret = strncpy_s(newgrp->ainfo.iopsread, + sizeof(newgrp->ainfo.iopsread), + oldgrp->ainfo.iopsread, + sizeof(oldgrp->ainfo.iopsread) - 1); + securec_check_errno(sret, , ); + sret = strncpy_s(newgrp->ainfo.iopswrite, + sizeof(newgrp->ainfo.iopswrite), + oldgrp->ainfo.iopswrite, + sizeof(oldgrp->ainfo.iopswrite) - 1); + securec_check_errno(sret, , ); + sret = strncpy_s( + newgrp->ainfo.bpsread, sizeof(newgrp->ainfo.bpsread), oldgrp->ainfo.bpsread, sizeof(oldgrp->ainfo.bpsread) - 1); + securec_check_errno(sret, , ); + sret = strncpy_s(newgrp->ainfo.bpswrite, + sizeof(newgrp->ainfo.bpswrite), + oldgrp->ainfo.bpswrite, + sizeof(oldgrp->ainfo.bpswrite) - 1); + securec_check_errno(sret, , ); + + /* 设置异常信息 */ + for (int i = 0; i < EXCEPT_ALL_KINDS; ++i) { + newgrp->except[i].blocktime = oldgrp->except[i].blocktime; + newgrp->except[i].elapsedtime = oldgrp->except[i].elapsedtime; + newgrp->except[i].allcputime = oldgrp->except[i].allcputime; + newgrp->except[i].qualitime = oldgrp->except[i].qualitime; + newgrp->except[i].skewpercent = oldgrp->except[i].skewpercent; + } + + sret = strncpy_s(newgrp->cpuset, sizeof(newgrp->cpuset), oldgrp->cpuset, sizeof(oldgrp->cpuset) - 1); + securec_check_errno(sret, , ); + + newgrp->percent = (unsigned int)oldgrp->percent; +} + +/* + * 函数名称:cgconf_generate_file_by_root + * 功能描述:由root用户生成配置文件 + * + * 注意:配置文件必须存在于“etc”目录下 + * + * 参数: + * fsize: 配置文件大小 + * cfgpath: 配置文件路径 + * + * 相似应用实例:在需要由root用户生成配置文件的场景中,可以使用该函数。 + */ +int cgconf_generate_file_by_root(long fsize, char* cfgpath) +{ + void* vaddr = NULL; + errno_t sret; + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); + + /* 当删除配置文件时,必须确保它存在 */ + if (cgutil_opt.dflag && (-1 == fsize)) { + fprintf(stderr, "错误:用户%s不存在。\n", cgutil_opt.user); + free(cfgpath); + cfgpath = NULL; + return -1; + } + + /* 文件不存在,创建新的文件 */ + if (fsize == -1) { + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); + if (NULL == vaddr) { + fprintf(stderr, "错误:创建和映射配置文件%s失败!\n", cfgpath); + free(cfgpath); + cfgpath = NULL; + return -1; + } + + sret = memset_s(vaddr, cglen, 0, cglen); + securec_check_errno(sret, free(cfgpath), -1); + + /* 重写映射文件 */ + cgconf_generate_default_config_file(vaddr); + } + else { + fprintf(stderr, "错误:文件%s已损坏,请删除并重新创建。\n", cfgpath); + free(cfgpath); + cfgpath = NULL; + return -1; + } + + free(cfgpath); + cfgpath = NULL; + return 0; +} +/** + * function name: cgconf_generate_file_by_user + * 功能:通过非root用户生成配置文件 + * + * 注意:配置文件必须存在于"etc"目录中 + * + * 参数: + * - fsize: 配置文件大小 + * - cfgpath: 配置文件路径 + * + * 返回值: + * - 成功返回0,失败返回-1 + */ +int cgconf_generate_file_by_user(long fsize, char* cfgpath) +{ + void* vaddr = NULL; // 文件映射到内存的地址 + errno_t sret; // 用于保存返回值 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 配置文件大小 + + if (fsize == -1) { // 判断配置文件是否存在 + if ('\0' == cgutil_opt.nodegroup[0]) { // 判断是否为根用户 + fprintf(stderr, + "ERROR: 配置文件 %s 不存在!\n" + "HINT: 请通过root用户创建配置文件!\n", + cfgpath); + free(cfgpath); + cfgpath = NULL; + return -1; + } + + if (cgutil_opt.nodegroup[0] && 0 == cgutil_opt.cflag) { // 判断是否存在配置文件 + fprintf(stderr, + "ERROR: 配置文件 %s 不存在!\n" + "HINT: 请在使用之前创建配置文件!\n", + cfgpath); + free(cfgpath); + cfgpath = NULL; + return -1; + } + else { + /* 将原始集群更改为虚拟集群 */ + if (cgutil_opt.rename) { // 判断是否需要重命名配置文件 + int old_cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + + sizeof(GSCFG_SUFFIX) + 1; + char* old_cfgpath = (char*)malloc(old_cfgpath_len); + if (old_cfgpath == NULL) { // 内存分配失败 + free(cfgpath); + cfgpath = NULL; + return -1; + } + + sret = snprintf_s(old_cfgpath, + old_cfgpath_len, + old_cfgpath_len - 1, + "%s/%s/%s_%s%s", + cgutil_opt.hpath, + GSCGROUP_CONF_DIR, + GSCFG_PREFIX, + cgutil_passwd_user->pw_name, + GSCFG_SUFFIX); + if (sret != EOK) { // 格式化字符串失败 + fprintf(stderr, "ERROR: 构造旧的cgroup配置文件路径失败"); + free(old_cfgpath); + old_cfgpath = NULL; + free(cfgpath); + cfgpath = NULL; + return -1; + } + + /* 将旧的配置文件路径重命名为新的配置文件路径 */ + int ret = rename(old_cfgpath, cfgpath); + if (ret != 0) { // 重命名失败 + fprintf(stderr, "ERROR: 将 %s 重命名为 %s 失败!\n", cfgpath, old_cfgpath); + free(old_cfgpath); + old_cfgpath = NULL; + free(cfgpath); + cfgpath = NULL; + return -1; + } + + /* 重置配置文件路径 */ + free(cfgpath); + cfgpath = NULL; + cfgpath = old_cfgpath; + } + + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); // 将配置文件映射到内存 + if (NULL == vaddr) { // 文件映射失败 + fprintf(stderr, "ERROR: 创建并映射配置文件 %s 失败!\n", cfgpath); + free(cfgpath); + cfgpath = NULL; + return -1; + } + + sret = memset_s(vaddr, cglen, 0, cglen); // 清空映射文件 + securec_check_errno(sret, free(cfgpath), -1); + /* 重新生成映射文件的内容 */ + cgconf_generate_default_config_file(vaddr); + } + } + else { + fprintf(stderr, + "ERROR: 配置文件大小与当前cgroup不匹配!\n" + "HINT: 请删除配置文件 %s 并重新创建!\n", + cfgpath); + free(cfgpath); + cfgpath = NULL; + return -1; + } + + free(cfgpath); + cfgpath = NULL; + return 0; +} +/** + * function name: cgconf_parse_nodegroup_config_file + * description : 解析配置文件并设置全局变量 + * + * Note: 配置文件必须存在于"etc"目录中 + */ + +int cgconf_parse_nodegroup_config_file(void) +{ + long fsize = 0; // 配置文件的大小 + void* vaddr = NULL; // 配置文件的虚拟地址 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 配置文件的大小 + int i = 0; // 循环变量 + char* cfgpath = NULL; // 配置文件的路径 + size_t cfgpath_len; // 配置文件路径的长度 + errno_t sret; // 错误码 + + if ('\0' == cgutil_opt.nodegroup[0]) { // 如果nodegroup未指定 + fprintf(stderr, "ERROR: the nodegroup should be specified!\n"); // 输出错误信息 + return -1; // 返回错误码 + } + + cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + 1 + + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; // 计算配置文件路径的长度 + cfgpath = (char*)malloc(cfgpath_len); // 分配配置文件路径的内存空间 + if (cfgpath == NULL) { + return -1; + } + + /* 获取etc目录 */ + sret = snprintf_s(cfgpath, + cfgpath_len, + cfgpath_len - 1, + "%s/%s/%s.%s_%s%s", + cgutil_opt.hpath, + GSCGROUP_CONF_DIR, + cgutil_opt.nodegroup, + GSCFG_PREFIX, + cgutil_passwd_user->pw_name, + GSCFG_SUFFIX); + securec_check_intval(sret, free(cfgpath), -1); + /* 获取配置文件 */ + fsize = gsutil_filesize(cfgpath); // 获取配置文件的大小 + /* 配置文件不存在或大小不匹配 */ + if (-1 == fsize || fsize != (long)cglen) { + fprintf(stderr, + "ERROR: the nodegroup configure file doesn't exist or " + "the size of the nodegroup configure file doesn't match!\n"); // 输出错误信息 + free(cfgpath); + cfgpath = NULL; + return -1; // 返回错误码 + } + + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); // 将配置文件映射到内存 + if (NULL == vaddr) { + fprintf(stderr, "failed to create and map the configure file %s!\n", cfgpath); // 输出错误信息 + free(cfgpath); + cfgpath = NULL; + return -1; // 返回错误码 + } + + for (i = 0; i < GSCGROUP_ALLNUM; i++) { + cgutil_vaddr[i] = (gscgroup_grp_t*)vaddr + i; // 设置全局变量cgutil_vaddr[i]的值为vaddr + i + + if (i == TOPCG_CLASS) { + sret = strcpy_s(cgutil_vaddr[i]->grpname, GPNAME_LEN, cgutil_opt.nodegroup); // 将nodegroup复制到cgutil_vaddr[i]->grpname + securec_check_intval(sret, free(cfgpath), -1); + } + } + + free(cfgpath); + cfgpath = NULL; + return 0; +} + +/** + * 示例: + * cgutil_opt.nodegroup = "group1" + * cgutil_opt.hpath = "/path/to" + * cgutil_passwd_user->pw_name = "user1" + * + * 配置文件路径计算结果: + * "/path/to/etc/group1.cg_user1" + */ + /* + * 函数名:cgconf_get_config_path + * 描述:获取配置文件路径 + * + * 注意:配置文件必须存在于 "etc" 目录下 + */ + +char* cgconf_get_config_path(bool backup) +{ + char* cfgpath = NULL; // 配置文件路径 + size_t cfgpath_len; // 配置文件路径的长度 + errno_t sret; + + if ('\0' == cgutil_opt.nodegroup[0]) { // 如果节点组为空 + if (false == backup) { // 如果不需要备份 + cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + sizeof(GSCFG_PREFIX) + 1 + + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; + } + else { // 如果需要备份 + cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + sizeof(GSCFG_PREFIX) + 1 + + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + sizeof(GSCFG_BACKUP) + 1; + } + } + else { // 如果节点组不为空 + if (false == backup) { // 如果不需要备份 + cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + + 1 + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; + } + else { // 如果需要备份 + cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + + 1 + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + + sizeof(GSCFG_BACKUP) + 1; + } + } + + cfgpath = (char*)malloc(cfgpath_len); // 分配内存存放配置文件路径 + if (cfgpath == NULL) { // 内存分配失败,返回NULL + return NULL; + } + + /* 获取etc目录 */ + if ('\0' == cgutil_opt.nodegroup[0]) { // 如果节点组为空 + if (false == backup) { // 如果不需要备份 + sret = snprintf_s(cfgpath, + cfgpath_len, + cfgpath_len - 1, + "%s/%s/%s_%s%s", + cgutil_opt.hpath, + GSCGROUP_CONF_DIR, + GSCFG_PREFIX, + cgutil_passwd_user->pw_name, + GSCFG_SUFFIX); + } + else { // 如果需要备份 + sret = snprintf_s(cfgpath, + cfgpath_len, + cfgpath_len - 1, + "%s/%s/%s_%s%s%s", + cgutil_opt.hpath, + GSCGROUP_CONF_DIR, + GSCFG_PREFIX, + cgutil_passwd_user->pw_name, + GSCFG_SUFFIX, + GSCFG_BACKUP); + } + } + else { // 如果节点组不为空 + if (false == backup) { // 如果不需要备份 + sret = snprintf_s(cfgpath, + cfgpath_len, + cfgpath_len - 1, + "%s/%s/%s.%s_%s%s", + cgutil_opt.hpath, + GSCGROUP_CONF_DIR, + cgutil_opt.nodegroup, + GSCFG_PREFIX, + cgutil_passwd_user->pw_name, + GSCFG_SUFFIX); + } + else { // 如果需要备份 + sret = snprintf_s(cfgpath, + cfgpath_len, + cfgpath_len - 1, + "%s/%s/%s.%s_%s%s%s", + cgutil_opt.hpath, + GSCGROUP_CONF_DIR, + cgutil_opt.nodegroup, + GSCFG_PREFIX, + cgutil_passwd_user->pw_name, + GSCFG_SUFFIX, + GSCFG_BACKUP); + } + } + + securec_check_intval(sret, free(cfgpath), NULL); // 检查字符串格式化函数返回值 + + return cfgpath; +} + +bool cgconf_gid_invalid(void) +{ + if (cgutil_vaddr[TOPCG_ROOT] == NULL || cgutil_vaddr[TOPCG_GAUSSDB] == NULL || + cgutil_vaddr[TOPCG_BACKEND] == NULL || cgutil_vaddr[BACKENDCG_START_ID] == NULL || + cgutil_vaddr[BACKENDCG_START_ID + 1] == NULL || cgutil_vaddr[TOPCG_CLASS] == NULL || + cgutil_vaddr[CLASSCG_START_ID] == NULL || cgutil_vaddr[WDCG_START_ID] == NULL || + cgutil_vaddr[TSCG_START_ID] == NULL) { + return true; + } + if (cgutil_vaddr[TOPCG_ROOT]->gid != TOPCG_ROOT || cgutil_vaddr[TOPCG_GAUSSDB]->gid != TOPCG_GAUSSDB || + cgutil_vaddr[TOPCG_BACKEND]->gid != TOPCG_BACKEND || + cgutil_vaddr[BACKENDCG_START_ID]->gid != BACKENDCG_START_ID || + cgutil_vaddr[BACKENDCG_START_ID + 1]->gid != (BACKENDCG_START_ID + 1) || + cgutil_vaddr[TOPCG_CLASS]->gid != TOPCG_CLASS || cgutil_vaddr[CLASSCG_START_ID]->gid != CLASSCG_START_ID || + cgutil_vaddr[WDCG_START_ID]->gid != WDCG_START_ID || cgutil_vaddr[TSCG_START_ID]->gid != TSCG_START_ID) { + return true; + } + return false; +} +/* + * 函数名:cgconf_parse_config_file + * 描述:解析配置文件并设置全局变量 + * 注意:配置文件必须存在于"etc"目录中 + */ +int cgconf_parse_config_file(void) +{ + long fsize = 0; // 文件大小 + void* vaddr = NULL; // 文件映射的虚拟地址 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // gscgroup_grp_t结构体的大小 + int i = 0; + int ret = -1; + char* cfgpath = NULL; // 配置文件路径 + + /* 获取配置文件路径 */ + cfgpath = cgconf_get_config_path(false); + if (NULL == cfgpath) { + return -1; + } + + fsize = gsutil_filesize(cfgpath); + /* 配置文件不存在或大小不一致*/ + if (-1 == fsize || fsize != (long)cglen) { + if (geteuid() == 0) { + ret = cgconf_generate_file_by_root(fsize, cfgpath); + } + else { + if (cgutil_opt.cflag && *cgutil_opt.nodegroup && *cgutil_opt.clsname == '\0') + ret = cgconf_generate_file_by_user(fsize, cfgpath); + else if (*cgutil_opt.nodegroup) { + free(cfgpath); + cfgpath = NULL; + fprintf(stderr, "ERROR: the specified node group %s doesn't exist!\n", cgutil_opt.nodegroup); + } + else { + free(cfgpath); + cfgpath = NULL; + } + } + + if (-1 == ret) { + return -1; /* cfgpath已经被释放,这里不需要再释放 */ + } + } + else { + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); + + if (NULL == vaddr) { + fprintf(stderr, "failed to create and map the configure file %s!\n", cfgpath); + free(cfgpath); + cfgpath = NULL; + return -1; + } + + for (i = 0; i < GSCGROUP_ALLNUM; i++) { + cgutil_vaddr[i] = (gscgroup_grp_t*)vaddr + i; + if (cgutil_vaddr[i]->gid >= GSCGROUP_ALLNUM) { + fprintf(stderr, "cgroup gid in configure file %s is out of range !\n", cfgpath); + free(cfgpath); + cfgpath = NULL; + return -1; + } + } + if (cgconf_gid_invalid()) { + fprintf(stderr, "cgroup gid in configure file %s is invalid !\n", cfgpath); + free(cfgpath); + cfgpath = NULL; + return -1; + } + + free(cfgpath); + cfgpath = NULL; + } + return 0; +} + +/* + * 函数名:cgconf_map_nodegroup_conffile + * 描述:返回原始配置文件的映射信息 + * + */ +void* cgconf_map_nodegroup_conffile(void) +{ + long fsize = 0; // 文件大小 + void* vaddr = NULL; // 文件映射的虚拟地址 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // gscgroup_grp_t结构体的大小 + char* cfgpath = NULL; // 配置文件路径 + size_t cfgpath_len; + errno_t sret; + cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + strlen(cgutil_opt.nodegroup) + 1 + + sizeof(GSCFG_PREFIX) + 1 + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; + + cfgpath = (char*)malloc(cfgpath_len); + if (NULL == cfgpath) { + return NULL; + } + + /* 获取etc目录 */ + sret = snprintf_s(cfgpath, + cfgpath_len, + cfgpath_len - 1, + "%s/%s/%s.%s_%s%s", + cgutil_opt.hpath, + GSCGROUP_CONF_DIR, + cgutil_opt.nodegroup, + GSCFG_PREFIX, + cgutil_passwd_user->pw_name, + GSCFG_SUFFIX); + securec_check_intval(sret, free(cfgpath), NULL); + fsize = gsutil_filesize(cfgpath); + /* 恢复时确保文件存在 */ + if (-1 == fsize) { + free(cfgpath); + cfgpath = NULL; + return NULL; + } + + /* 配置文件不存在或大小不一致 */ + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); + free(cfgpath); + cfgpath = NULL; + + return vaddr; +} +/** +函数名称:cgconf_map_origin_conffile +函数描述:返回原始配置文件的映射信息 +//该函数的作用是将原始配置文件映射到内存中,并返回映射信息的地址。 +//函数变量如下: +//fsize : 用于保存文件大小。 +//vaddr : 用于保存映射信息的地址。 +//cglen : 配置文件映射长度,根据GSCGROUP_ALLNUM和gscgroup_grp_t结构体的大小计算得到。 +//cfgpath : 用于保存配置文件路径。 +//cfgpath_len : 配置文件路径的长度。 +//函数内部的操作步骤如下: +//计算配置文件路径的长度。 +//分配内存空间用于存储配置文件路径。 +//判断内存分配是否成功,若失败则返回NULL。 +//使用snprintf_s构建完整的配置文件路径。 +//检查snprintf_s函数返回值,若返回值不为0,则释放cfgpath内存并返回NULL。 +//获取配置文件的大小。 +//若文件大小为 - 1,表示文件不存在或无法访问,则释放cfgpath内存并返回NULL。 +//将配置文件映射到内存中,使用gsutil_filemap函数实现。 +//释放cfgpath内存。 +//返回映射信息的地址。 +//类似应用实例: 该函数常用于配置文件操作中,用于将配置文件映射到内存中,以方便读取和修改配置参数。在映射完成后,可以通过操作映射后的内存来修改配置参数,并将更改后的配置写回到配置文件中,从而实现对配置文件的动态修改。 +*/ + +void* cgconf_map_origin_conffile(void) { + long fsize = 0; // 文件大小 void* vaddr = NULL; // 映射信息的地址 size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 配置文件映射长度 char* cfgpath = NULL; // 配置文件路径 size_t cfgpath_len; // 配置文件路径长度 errno_t sret; + + cfgpath_len = strlen(cgutil_opt.hpath) + 1 + sizeof(GSCGROUP_CONF_DIR) + 1 + sizeof(GSCFG_PREFIX) + 1 + + strlen(cgutil_passwd_user->pw_name) + sizeof(GSCFG_SUFFIX) + 1; // 计算配置文件路径长度 + + cfgpath = (char*)malloc(cfgpath_len); // 分配内存空间存储配置文件路径 + if (NULL == cfgpath) { + return NULL; + } + + // 构建完整的配置文件路径 + sret = snprintf_s(cfgpath, + cfgpath_len, + cfgpath_len - 1, + "%s/%s/%s_%s%s", + cgutil_opt.hpath, + GSCGROUP_CONF_DIR, + GSCFG_PREFIX, + cgutil_passwd_user->pw_name, + GSCFG_SUFFIX); + + securec_check_intval(sret, free(cfgpath), NULL); + fsize = gsutil_filesize(cfgpath); // 获取配置文件大小 + + // 确保在恢复时文件存在 + if (-1 == fsize) { + free(cfgpath); + cfgpath = NULL; + return NULL; + } + + // 配置文件不存在或者大小不一致 + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); // 文件映射 + free(cfgpath); + cfgpath = NULL; + + return vaddr; +} + +/** + * 函数名称:cgconf_map_backup_conffile + * 函数描述:返回备份文件的映射信息 + * + * 参数: + * flag - 标志位,用于指示是否进行恢复操作 + * + * 返回值: + * 映射信息的地址 + * + * 说明: + * 该函数根据传入的标志位,返回备份文件的映射信息的地址。 + */ + +void* cgconf_map_backup_conffile(bool flag) +{ + long fsize = 0; // 文件大小 + void* vaddr = NULL; // 映射信息的地址 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 配置文件映射长度 + char* cfgpath = NULL; // 配置文件路径 + + /* 获取配置文件路径 */ + cfgpath = cgconf_get_config_path(true); + if (NULL == cfgpath) { + return NULL; + } + + fsize = gsutil_filesize(cfgpath); // 获取配置文件大小 + + // 在恢复操作时确保文件存在 + if (flag == false && -1 == fsize) { + free(cfgpath); + cfgpath = NULL; + return NULL; + } + + // 配置文件不存在或者大小不一致 + vaddr = gsutil_filemap(cfgpath, cglen, (PROT_READ | PROT_WRITE), MAP_SHARED, cgutil_passwd_user); // 文件映射 + free(cfgpath); + cfgpath = NULL; + + return vaddr; +} + +/** + * 函数名称:cgconf_backup_config_file + * 函数描述:在创建/删除/更新cgroups时备份配置文件 + * + * 返回值: + * 0 - 备份成功 + * -1 - 备份失败 + * + * 说明: + * 该函数在创建/删除/更新cgroups时备份配置文件,并返回备份结果。 + * 注意:配置文件必须存在于"etc"目录中。 + */ +//类似应用实例: +//在创建、删除或更新cgroups时,为了避免配置文件丢失或损坏,常常需要备份配置文件。使用该函数可以备份配置文件,并将备份内容映射到内存中,以便于对备份文件进行修改或者恢复操作。备份文件的映射信息可以方便地进行读取、修改和写回操作,从而实现对配置文件的安全备份和恢复。 +// +//代码块功能解释: +//1. 判断当前用户是否为root用户,若是则直接返回,不进行备份操作。 +//2. 调用cgconf_map_backup_conffile函数映射备份文件,并获取映射信息的地址。 +//3. 判断映射信息的地址是否为空,若为空则输出错误信息并返回备份失败。 +//4. 使用memcpy_s函数将映射信息中的配置文件备份到映射备份文件的内存空间中。 +//5. 使用munmap函数取消对映射信息的映射。 +//6. 返回备份成功。 +// +//注意事项: +//1. 该函数在普通用户下执行,只有root用户才能执行备份操作。 +//2. 备份的配置文件必须位于"etc"目录中。 +//3. 使用memcpy_s进行备份操作时,需要确保目标地址的内存空间足够,避免内存溢出。 +//4. 备份配置文件后,可以根据需要对配置文件的备份进行修改或恢复操作。 + +int cgconf_backup_config_file(void) +{ + void* vaddr = NULL; // 映射信息的地址 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 配置文件映射长度 + errno_t sret; + + // 如果当前用户是root用户,则直接返回,不进行备份操作 + if (geteuid() == 0) { + return 0; + } + + vaddr = cgconf_map_backup_conffile(true); // 映射备份文件 + if (NULL == vaddr) { + fprintf(stderr, "failed to create and map the backup configure file!\n"); + return -1; + } + + sret = memcpy_s(vaddr, cglen, cgutil_vaddr[0], cglen); // 备份配置文件 + securec_check_errno(sret, (void)munmap(vaddr, cglen); , -1); + + (void)munmap(vaddr, cglen); // 取消映射 + + return 0; +} + +/** + * 函数名称:cgconf_remove_backup_conffile + * 函数描述:删除创建/删除/更新cgroups时备份的配置文件 + * + * 注意:配置文件必须存在于"etc"目录中 + */ +void cgconf_remove_backup_conffile(void) +{ + char* cfgpath = NULL; + + // 如果当前用户是root用户,则不执行删除操作 + if (geteuid() == 0) { + return; + } + + // 获取配置文件路径 + cfgpath = cgconf_get_config_path(true); + // 如果获取路径失败,则不执行删除操作 + if (NULL == cfgpath) { + return; + } + + // 删除配置文件 + (void)unlink(cfgpath); + + // 释放内存并将指针置空 + free(cfgpath); + cfgpath = NULL; +} + +// 宏定义,显示CPU配额信息和核心信息 +#define CGCONFIG_DISPLAY_CPU_QUOTA(cg) \ + { \ + if ((cg) && (cg)->ainfo.quota) { \ + fprintf(stdout, " Quota(%%): %2d", cgutil_vaddr[i]->ainfo.quota); \ + } \ + fprintf(stdout, " Cores: %s", cgutil_vaddr[i]->cpuset); \ + } + +/** + * 函数名称:cgconf_display_exception_detail + * 函数描述:显示组异常详细信息 + * + * 参数: + * - gid:组ID + * - kinds:异常种类数量 + */ +static void cgconf_display_exception_detail(int gid, int kinds) +{ + int i = 0; + + // 遍历异常种类 + for (i = 0; i < kinds; ++i) { + // 如果异常种类不合法,则跳过本次循环 + if (gsutil_exception_kind_is_valid(cgutil_vaddr[gid], i) == 0) + continue; + + // 如果是中断异常 + if (i == EXCEPT_ABORT) { + fprintf(stdout, "%s: ", gsutil_print_exception_flag(i)); + // 如果块时间大于0,输出块时间 + if (cgutil_vaddr[gid]->except[i].blocktime > 0) + fprintf(stdout, "BlockTime=%u ", cgutil_vaddr[gid]->except[i].blocktime); + // 如果经过时间大于0,输出经过时间 + if (cgutil_vaddr[gid]->except[i].elapsedtime > 0) + fprintf(stdout, "ElapsedTime=%u ", cgutil_vaddr[gid]->except[i].elapsedtime); + // 如果溢出大小大于0,输出溢出大小 + if (cgutil_vaddr[gid]->except[i].spillsize > 0) + fprintf(stdout, "SpillSize=%ld ", cgutil_vaddr[gid]->except[i].spillsize); + // 如果广播大小大于0,输出广播大小 + if (cgutil_vaddr[gid]->except[i].broadcastsize > 0) + fprintf(stdout, "BroadcastSize=%ld ", cgutil_vaddr[gid]->except[i].broadcastsize); + // 如果总CPU时间大于0,输出总CPU时间 + if (cgutil_vaddr[gid]->except[i].allcputime > 0) + fprintf(stdout, "AllCpuTime=%u ", cgutil_vaddr[gid]->except[i].allcputime); + // 如果限制时间大于0,输出限制时间 + if (cgutil_vaddr[gid]->except[i].qualitime > 0) + fprintf(stdout, "QualificationTime=%u ", cgutil_vaddr[gid]->except[i].qualitime); + // 如果CPU偏差百分比大于0,输出CPU偏差百分比 + if (cgutil_vaddr[gid]->except[i].skewpercent > 0) + fprintf(stdout, "CPUSkewPercent=%u ", cgutil_vaddr[gid]->except[i].skewpercent); + } + else { + fprintf(stdout, "%s: ", gsutil_print_exception_flag(i)); + // 如果总CPU时间大于0,输出总CPU时间 + if (cgutil_vaddr[gid]->except[i].allcputime > 0) + fprintf(stdout, "AllCpuTime=%u ", cgutil_vaddr[gid]->except[i].allcputime); + // 如果限制时间大于0,输出限制时间 + if (cgutil_vaddr[gid]->except[i].qualitime > 0) + fprintf(stdout, "QualificationTime=%u ", cgutil_vaddr[gid]->except[i].qualitime); + // 如果CPU偏差百分比大于0,输出CPU偏差百分比 + if (cgutil_vaddr[gid]->except[i].skewpercent > 0) + fprintf(stdout, "CPUSkewPercent=%u ", cgutil_vaddr[gid]->except[i].skewpercent); + } + + fprintf(stdout, "\n"); + } +} +//* +*函数名:cgconf_display_exception +* 功能:显示组异常信息 +* +* / + +static void cgconf_display_exception(void) +{ + int cls = 0; // 类别变量,用于循环遍历类别ID + int wd = 0; // 节点变量,用于循环遍历节点ID + int flag = 0; // 标志变量,判断是否已显示类别信息 + int pflag = 0; // 标志变量,判断是否有异常信息 + int kinds = EXCEPT_ALL_KINDS; // 异常类型,此处设为所有异常种类 + + fprintf(stdout, "\n\nList of group exception information:"); + + /* 检查类别是否存在 */ + for (cls = CLASSCG_START_ID; cls <= CLASSCG_END_ID; cls++) { + if (cgutil_vaddr[cls]->used == 0) { + continue; + } + + flag = 0; + + if (gsutil_exception_is_valid(cgutil_vaddr[cls], kinds) != 0) { + fprintf(stdout, + "\nGID: %3d Type: %-6s Class: %-16s\n", + cgutil_vaddr[cls]->gid, + "EXCEPTION", + cgutil_vaddr[cls]->grpname); + + cgconf_display_exception_detail(cls, kinds); + + ++flag; + ++pflag; + } + + for (wd = WDCG_START_ID; wd <= WDCG_END_ID; wd++) { + if (cgutil_vaddr[wd]->used == 0 || cgutil_vaddr[wd]->ginfo.wd.cgid != cls || + gsutil_exception_is_valid(cgutil_vaddr[wd], kinds) == 0) + continue; + + if (flag == 0) { + /* 显示类别的组信息 */ + fprintf(stdout, + "\nGID: %3d Type: %-6s Class: %-16s", + cgutil_vaddr[cls]->gid, + "EXCEPTION", + cgutil_vaddr[cls]->grpname); + ++flag; + } + + fprintf(stdout, + "\nGID: %3d Type: %-6s Group: %s:%-16s\n", + cgutil_vaddr[wd]->gid, + "EXCEPTION", + cgutil_vaddr[cls]->grpname, + cgutil_vaddr[wd]->grpname); + + cgconf_display_exception_detail(wd, kinds); + + ++pflag; + } + } + + if (pflag == 0) { + fprintf(stdout, "\n"); + } +} + +/* + * 函数名:cgconf_display_exception_detail + * 功能:显示组异常详细信息 + * 参数: + * - int id: 组件ID + * - int kinds: 异常类型 + * + */ + +void cgconf_display_exception_detail(int id, int kinds) +{ + int i = 0; // 计数变量 + int j = 0; // 计数变量 + + for (i = 0; i < EXCEPTION_MAX; i++) { + if (cgutil_vaddr[id]->excpt[i].ex_type != 0 && + (kinds & cgutil_vaddr[id]->excpt[i].ex_type) != 0) { + fprintf(stdout, "\t%-20s: %-15s", + cgutil_vaddr[id]->excpt[i].ex_name, + (cgutil_vaddr[id]->excpt[i].eflags & CGUTIL_EXCEPT_ENABLE) ? "enabled" : "disabled"); + fprintf(stdout, "\t\t- "); + for (j = 0; j < sizeof(cg_exception_descriptions) / sizeof(cg_exception_descriptions[0]); j++) { + if (cg_exception_descriptions[j].cge_code == cgutil_vaddr[id]->excpt[i].ex_code) { + fprintf(stdout, "%s\n", cg_exception_descriptions[j].cge_cause); + break; + } + } + } + } +} + +/* + * 函数名:gsutil_exception_is_valid + * 功能:检查异常是否有效 + * 参数: + * - struct cgutil_vaddr_t *cgv: 组件V地址 + * - int kinds: 异常类型 + * 返回值:如果异常有效,则返回非零值;否则返回0 + * + */ +//以上是一个用于显示组异常信息的代码。代码首先定义了一些变量,并打印了提示信息。 +//然后,代码通过循环遍历每个类别ID,检查类别是否存在,如果存在则继续执行。 +//在每个类别ID的循环中,代码首先检查该类别是否包含有效异常,如果包含则打印类别的信息,并调用cgconf_display_exception_detail函数显示该类别的详细异常信息。 +//接下来,代码通过循环遍历每个节点ID,检查节点是否存在并且属于当前类别,同时判断节点是否包含有效异常。如果节点包含有效异常,则打印节点的信息,并调用cgconf_display_exception_detail函数显示节点的详细异常信息。 +//最后,如果没有异常信息,则打印空行。 +//cgconf_display_exception_detail函数用于显示组异常的详细信息。函数通过循环遍历异常数组,检查异常类型是否有效,并根据异常类型打印相应的信息。 +//gsutil_exception_is_valid函数用于检查异常是否有效。函数通过循环遍历异常数组,判断异常类型是否有效,并返回相应的结果。 +//该代码可以用于显示某个系统中的组异常信息,例如一个计算机集群系统中,可以用于显示各个节点的异常信息,帮助管理员及时发现并解决问题。 + +int gsutil_exception_is_valid(struct cgutil_vaddr_t* cgv, int kinds) +{ + int i = 0; // 计数变量 + + for (i = 0; i < EXCEPTION_MAX; i++) { + if (cgv->excpt[i].ex_type != 0 && (kinds & cgutil_vaddr[i].excpt[i].ex_type) != 0) { + return 1; // 异常有效 + } + } + + return 0; // 异常无效 +} +/* + * 函数名称:cgconf_display_groups + * 功能描述:显示配置文件信息 + * + */ +void cgconf_display_groups(void) +{ + int i; + + /* 显示顶层组信息 */ + fprintf(stdout, "\nTop Group information is listed:"); + + for (i = 0; i <= TOPCG_END_ID; i++) { + // 如果nodegroup不为空且当前遍历到的组不是TOPCG_CLASS组,则跳过 + if ('\0' != cgutil_opt.nodegroup[0] && i != TOPCG_CLASS) + continue; + + fprintf(stdout, + "\nGID: %3d Type: %-6s Percent(%%): %4u(%3d) Name: %-20s ", + cgutil_vaddr[i]->gid, + cgconf_get_group_type(cgutil_vaddr[i]->gtype), + cgutil_vaddr[i]->percent, + cgutil_vaddr[i]->ginfo.top.percent, + cgutil_vaddr[i]->grpname); + + CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); + } + + /* 显示Backend组信息 */ + if ('\0' == cgutil_opt.nodegroup[0]) + fprintf(stdout, "\n\nBackend Group information is listed:"); + + for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { + // 如果该组未被使用或者nodegroup不为空,则跳过 + if (0 == cgutil_vaddr[i]->used || '\0' != cgutil_opt.nodegroup[0]) + continue; + + fprintf(stdout, + "\nGID: %3d Type: %-6s Name: %-16s " + "TopGID: %3d Percent(%%): %3u(%2d)", + cgutil_vaddr[i]->gid, + cgconf_get_group_type(cgutil_vaddr[i]->gtype), + cgutil_vaddr[i]->grpname, + cgutil_vaddr[i]->ginfo.cls.tgid, + cgutil_vaddr[i]->percent, + cgutil_vaddr[i]->ginfo.cls.percent); + + CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); + } + + /* 显示Class组信息 */ + fprintf(stdout, "\n\nClass Group information is listed:"); + + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + // 如果该组未被使用,则跳过 + if (0 == cgutil_vaddr[i]->used) + continue; + + fprintf(stdout, + "\nGID: %3d Type: %-6s Name: %-16s TopGID: %3d " + "Percent(%%): %3u(%2d) MaxLevel: %d RemPCT: %3d", + cgutil_vaddr[i]->gid, + cgconf_get_group_type(cgutil_vaddr[i]->gtype), + cgutil_vaddr[i]->grpname, + cgutil_vaddr[i]->ginfo.cls.tgid, + cgutil_vaddr[i]->percent, + cgutil_vaddr[i]->ginfo.cls.percent, + cgutil_vaddr[i]->ginfo.cls.maxlevel, + cgutil_vaddr[i]->ginfo.cls.rempct); + + CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); + } + + /* 显示Workload组信息 */ + fprintf(stdout, "\n\nWorkload Group information is listed:"); + + for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { + // 如果该组未被使用或者组名与GSCGROUP_TOP_WORKLOAD相同,则跳过 + if (0 == cgutil_vaddr[i]->used || + 0 == strncmp(cgutil_vaddr[i]->grpname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1)) + continue; + + fprintf(stdout, + "\nGID: %3d Type: %-6s Name: %-16s ClsGID: %3d " + "Percent(%%): %3u(%2d) WDLevel: %2d", + cgutil_vaddr[i]->gid, + cgconf_get_group_type(cgutil_vaddr[i]->gtype), + cgutil_vaddr[i]->grpname, + cgutil_vaddr[i]->ginfo.wd.cgid, + cgutil_vaddr[i]->percent, + cgutil_vaddr[i]->ginfo.wd.percent, + cgutil_vaddr[i]->ginfo.wd.wdlevel); + + CGCONFIG_DISPLAY_CPU_QUOTA(cgutil_vaddr[i]); + } + + /* 显示Timeshare组信息 */ + fprintf(stdout, "\n\nTimeshare Group information is listed:"); + + for (i = TSCG_START_ID; i <= TSCG_END_ID; i++) { + fprintf(stdout, + "\nGID: %3d Type: %-6s Name: %-16s Rate: %d", + cgutil_vaddr[i]->gid, + cgconf_get_group_type(cgutil_vaddr[i]->gtype), + cgutil_vaddr[i]->grpname, + cgutil_vaddr[i]->ginfo.ts.rate); + } + + // 显示异常组信息 + cgconf_display_exception(); + + fprintf(stdout, "\n"); + (void)fflush(stdout); +} -- 2.34.1 From a35d773da04a4008931b42f8e2cbd419687d50f7 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:00:30 +0800 Subject: [PATCH 03/56] Delete 'src/bin/gs_cgroup/cgexcp.cpp' --- src/bin/gs_cgroup/cgexcp.cpp | 314 ----------------------------------- 1 file changed, 314 deletions(-) delete mode 100644 src/bin/gs_cgroup/cgexcp.cpp diff --git a/src/bin/gs_cgroup/cgexcp.cpp b/src/bin/gs_cgroup/cgexcp.cpp deleted file mode 100644 index 585d67302..000000000 --- a/src/bin/gs_cgroup/cgexcp.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2019 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * cgexcp.cpp - * Cgroup exceptional data process - * - * IDENTIFICATION - * src/bin/gs_cgroup/cgexcp.cpp - * - * ------------------------------------------------------------------------- - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cgutil.h" - -#define EXCP_PARSE_KEY(p, val) \ - { \ - char *tmp = NULL, *bad = NULL; \ - tmp = strchr(p, '='); \ - *tmp++ = '\0'; \ - val = (unsigned long)strtoul(tmp, &bad, 10); \ - if (*tmp == '\0' || (bad && *bad)) { \ - fprintf(stderr, "ERROR: Exception format string, value \"%s\" is invalid!\n", (*tmp == '\0') ? " " : tmp); \ - return (-1); \ - } \ - } - -/* - * function name: cgexcp_skewpercent_is_invalid - * description : check skew percent whether is invalid - * return value : 0: valid, 1: invalid - */ -static int cgexcp_skewpercent_is_invalid(const except_data_t* except) -{ - if ((except->skewpercent > 0 && except->qualitime > 0) || (except->skewpercent <= 0 && except->qualitime <= 0)) - return 0; - - return 1; -} - -/* - * function name: cgexcp_exception_save - * description : save the exceptional data into the config file - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexcp_exception_save(gscgroup_grp_t* grp) -{ - char *p = NULL; - char *q = NULL; - char eflag; - unsigned long val; - int err = 0; - p = cgutil_opt.edata; - eflag = cgutil_opt.eflag; - - do { - while (*p == ' ') { - p++; - } - - q = strchr(p, ','); - if (q != NULL) { - *q++ = '\0'; - } - if (strncasecmp("BlockTime=", p, sizeof("BlockTime=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'BlockTime\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { - fprintf(stderr, - "ERROR: threshold \'BlockTime\' " - "for \"penalty\" is invalid!\n"); - err = -1; - break; - } - - grp->except[eflag - 1].blocktime = (unsigned int)val; - } else if (strncasecmp("ElapsedTime=", p, sizeof("ElapsedTime=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'ElapsedTime\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { - fprintf(stderr, - "ERROR: threshold \'ElapsedTime\', " - "for \"penalty\" is invalid!\n"); - err = -1; - break; - } - - grp->except[eflag - 1].elapsedtime = (unsigned int)val; - } else if (strncasecmp("SpillSize=", p, sizeof("SpillSize=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'SpillSize\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { - fprintf(stderr, - "ERROR: threshold \'SpillSize\', " - "for \"penalty\" is invalid!\n"); - err = -1; - break; - } - - grp->except[eflag - 1].spoolsize = (int64)val; - } else if (strncasecmp("BroadcastSize=", p, sizeof("BroadcastSize=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'BroadcastSize\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { - fprintf(stderr, - "ERROR: threshold \'BroadcastSize\', " - "for \"penalty\" is invalid!\n"); - err = -1; - break; - } - - grp->except[eflag - 1].broadcastsize = (int64)val; - } else if (strncasecmp("AllCpuTime=", p, sizeof("AllCpuTime=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'AllCpuTime\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - grp->except[eflag - 1].allcputime = (unsigned int)val; - } else if (strncasecmp("QualificationTime=", p, sizeof("QualificationTime=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > UINT_MAX) { - fprintf(stderr, - "ERROR: threshold \'QualificationTime\', " - "value limit exceeded, it should be 0~%u!\n", - UINT_MAX); - err = -1; - break; - } - - grp->except[eflag - 1].qualitime = (unsigned int)val; - } else if (strncasecmp("CPUSkewPercent=", p, sizeof("CPUSkewPercent=") - 1) == 0) { - EXCP_PARSE_KEY(p, val); - - if (val > 100) { - fprintf(stderr, - "ERROR: threshold \'CPUSkewPercent\', " - "value '%u' is invalid, it must be 0~100!\n", - (unsigned int)val); - err = -1; - break; - } - - grp->except[eflag - 1].skewpercent = (unsigned int)val; - } else { - fprintf(stderr, "ERROR: exception key string '%s' doesn't be supported!\n", p); - err = -1; - break; - } - p = q; - } while ((q != NULL) && *q); - - if (cgexcp_skewpercent_is_invalid(&grp->except[eflag - 1])) { - grp->except[eflag - 1].skewpercent = 0; - grp->except[eflag - 1].qualitime = 0; - fprintf(stderr, - "ERROR: exception key string '%s' is invalid, " - "\'CPUSkewPercent\' must be specified together with \'QualificationTime\'!\n", - cgutil_opt.edata); - - return -1; - } - - return err; -} - -/* - * function name: cgexcp_class_exception - * description : deal with the class exception - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexcp_class_exception(void) -{ - int i; - int cls = 0; - int wd = 0; - int cmp = -1; - char* tmpstr = NULL; - size_t wdname_len; - - /* check if the class exists */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { - cls = i; - break; - } - } - - /* back up the config file */ - if (-1 == cgconf_backup_config_file()) { - return -1; - } - - if (cls) { - if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_ABORT) && cgutil_opt.wdname[0]) { - wdname_len = strlen(cgutil_opt.wdname); - tmpstr = strchr(cgutil_opt.wdname, ':'); - - for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0 || cgutil_vaddr[i]->ginfo.wd.cgid != cls) - continue; - - /* workload name with level or no level */ - if (tmpstr != NULL) - cmp = strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname); - else { - if (':' == cgutil_vaddr[i]->grpname[wdname_len]) - cmp = strncmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname, wdname_len); - } - - if (cmp == 0) { - wd = i; - break; - } - } - - if (wd) { - if (-1 == cgexcp_exception_save(cgutil_vaddr[wd])) { - cgconf_remove_backup_conffile(); - return -1; - } - } else { - fprintf(stderr, "ERROR: the specified workload %s doesn't exist!\n", cgutil_opt.wdname); - cgconf_remove_backup_conffile(); - return -1; - } - } else { - if (-1 == cgexcp_exception_save(cgutil_vaddr[cls])) { - cgconf_remove_backup_conffile(); - return -1; - } - } - } else { - fprintf(stderr, "ERROR: the specified class %s doesn't exist!\n", cgutil_opt.clsname); - cgconf_remove_backup_conffile(); - return -1; - } - - return 0; -} -- 2.34.1 From 8cf2f058401999c7d4e8ee0482979f5fb54a1496 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:00:47 +0800 Subject: [PATCH 04/56] ADD file via upload --- src/bin/gs_cgroup/cgexcp.cpp | 273 +++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 src/bin/gs_cgroup/cgexcp.cpp diff --git a/src/bin/gs_cgroup/cgexcp.cpp b/src/bin/gs_cgroup/cgexcp.cpp new file mode 100644 index 000000000..e34d79e40 --- /dev/null +++ b/src/bin/gs_cgroup/cgexcp.cpp @@ -0,0 +1,273 @@ +/* + * 版权所有 (c) 2019华为技术有限公司 + * + * openGauss在Mulan PSL v2下获得许可。 + * 您可以根据Mulan PSL v2的条款和条件使用此软件。 + * 您可以在以下网址获得Mulan PSL v2的副本: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * 此软件基于"按原样"提供,没有任何形式的明示或暗示保证, + * 包括但不限于保证适销性、特定用途的适用性和非侵权性。 + * 有关更多详细信息,请参阅Mulan PSL v2。 + * ------------------------------------------------------------------------- + * + * cgexcp.cpp + * Cgroup异常数据处理 + * + * 标识 + * src/bin/gs_cgroup/cgexcp.cpp + * + * ------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cgutil.h" + +#define EXCP_PARSE_KEY(p, val) \ + { \ + char *tmp = NULL, *bad = NULL; \ + tmp = strchr(p, '='); \ + *tmp++ = '\0'; \ + val = (unsigned long)strtoul(tmp, &bad, 10); \ + if (*tmp == '\0' || (bad && *bad)) { \ + fprintf(stderr, "ERROR: Exception format string, value \"%s\" is invalid!\n", (*tmp == '\0') ? " " : tmp); \ + return (-1); \ + } \ + } + + /* + * 函数名:cgexcp_skewpercent_is_invalid + * 功能:检查偏移百分比是否无效 + * 返回值:0表示有效,1表示无效 + */ +static int cgexcp_skewpercent_is_invalid(const except_data_t* except) +{ + if ((except->skewpercent > 0 && except->qualitime > 0) || (except->skewpercent <= 0 && except->qualitime <= 0)) + return 0; + + return 1; +} +/** +* 函数名称:cgexcp_exception_save +* 描述:将异常数据保存到配置文件中 +* 返回值: +* -1:异常 +* 0:正常 +*/ +static int cgexcp_exception_save(gscgroup_grp_t* grp) { + char* p = NULL; // 保存解析字符串的指针 + char* q = NULL; // 保存','字符的指针 + char eflag; // 异常标志 + unsigned long val; // 异常值 + int err = 0; // 错误码 + + p = cgutil_opt.edata; // 获取解析字符串 + eflag = cgutil_opt.eflag; // 获取异常标志 + + do { + while (*p == ' ') { // 跳过空格字符 + p++; + } + + q = strchr(p, ','); // 查找','字符 + if (q != NULL) { + *q++ = '\0'; // 将','字符置为字符串结束符 + } + if (strncasecmp("BlockTime=", p, sizeof("BlockTime=") - 1) == 0) { // 判断是否为"BlockTime="字符串 + EXCP_PARSE_KEY(p, val); // 解析异常值 + + if (val > UINT_MAX) { // 判断异常值是否超出范围 + fprintf(stderr, + "ERROR: threshold \'BlockTime\', " + "value limit exceeded, it should be 0~%u!\n", + UINT_MAX); + err = -1; + break; + } + + if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { // 判断异常标志位是否为"penalty" + fprintf(stderr, + "ERROR: threshold \'BlockTime\' " + "for \"penalty\" is invalid!\n"); + err = -1; + break; + } + + grp->except[eflag - 1].blocktime = (unsigned int)val; // 将异常值保存到对应的异常数据结构中 + } + else if (strncasecmp("ElapsedTime=", p, sizeof("ElapsedTime=") - 1) == 0) { // 判断是否为"ElapsedTime="字符串 + EXCP_PARSE_KEY(p, val); // 解析异常值 + + if (val > UINT_MAX) { // 判断异常值是否超出范围 + fprintf(stderr, + "ERROR: threshold \'ElapsedTime\', " + "value limit exceeded, it should be 0~%u!\n", + UINT_MAX); + err = -1; + break; + } + + if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { // 判断异常标志位是否为"penalty" + fprintf(stderr, + "ERROR: threshold \'ElapsedTime\', " + "for \"penalty\" is invalid!\n"); + err = -1; + break; + } + + grp->except[eflag - 1].elapsedtime = (unsigned int)val; // 将异常值保存到对应的异常数据结构中 + } + else if (strncasecmp("SpillSize=", p, sizeof("SpillSize=") - 1) == 0) { // 判断是否为"SpillSize="字符串 + EXCP_PARSE_KEY(p, val); // 解析异常值 + + if (val > UINT_MAX) { // 判断异常值是否超出范围 + fprintf(stderr, + "ERROR: threshold \'SpillSize\', " + "value limit exceeded, it should be 0~%u!\n", + UINT_MAX); + err = -1; + break; + } + + if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { // 判断异常标志位是否为"penalty" + fprintf(stderr, + "ERROR: threshold \'SpillSize\', " + "for \"penalty\" is invalid!\n"); + err = -1; + break; + } + + grp->except[eflag - 1].spoolsize = (int64)val; // 将异常值保存到对应的异常数据结构中 + } + else if (strncasecmp("BroadcastSize=", p, sizeof("BroadcastSize=") - 1) == 0) { // 判断是否为"BroadcastSize="字符串 + EXCP_PARSE_KEY(p, val); // 解析异常值 + + if (val > UINT_MAX) { // 判断异常值是否超出范围 + fprintf(stderr, + "ERROR: threshold \'BroadcastSize\', " + "value limit exceeded, it should be 0~%u!\n", + UINT_MAX); + err = -1; + break; + } + + if (IS_EXCEPT_FLAG(eflag, EXCEPT_PENALTY)) { // 判断异常标志位是否为"penalty" + fprintf(stderr, + "ERROR: threshold \'BroadcastSize\', " + "for \"penalty\" is invalid!\n"); + err = -1; + break; + } + + grp->except[eflag - 1].broadcastsize = (int64)val; // 将异常值保存到对应的异常数据结构中 + } + } while (q != NULL); + + return err; // 返回错误码 +} +/* + * 函数名称:cgexcp_class_exception + * 功能描述:处理类异常 + * 返回值: + * -1:异常 + * 0:正常 + * + */ +int cgexcp_class_exception(void) +{ + int i; + int cls = 0; + int wd = 0; + int cmp = -1; + char* tmpstr = NULL; + size_t wdname_len; + + /* 检查类是否存在 */ + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0) + continue; + + if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { + cls = i; + break; + } + } + + /* 备份配置文件 */ + if (-1 == cgconf_backup_config_file()) { + return -1; + } + + if (cls) { + if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_ABORT) && cgutil_opt.wdname[0]) { + wdname_len = strlen(cgutil_opt.wdname); + tmpstr = strchr(cgutil_opt.wdname, ':'); + + for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0 || cgutil_vaddr[i]->ginfo.wd.cgid != cls) + continue; + + /* 判断工作负载名称是否有级别或者没有级别 */ + if (tmpstr != NULL) + cmp = strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname); + else { + if (':' == cgutil_vaddr[i]->grpname[wdname_len]) + cmp = strncmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname, wdname_len); + } + + if (cmp == 0) { + wd = i; + break; + } + } + + if (wd) { + if (-1 == cgexcp_exception_save(cgutil_vaddr[wd])) { + cgconf_remove_backup_conffile(); + return -1; + } + } + else { + fprintf(stderr, "错误:指定的工作负载 %s 不存在!\n", cgutil_opt.wdname); + cgconf_remove_backup_conffile(); + return -1; + } + } + else { + if (-1 == cgexcp_exception_save(cgutil_vaddr[cls])) { + cgconf_remove_backup_conffile(); + return -1; + } + } + } + else { + fprintf(stderr, "错误:指定的类 %s 不存在!\n", cgutil_opt.clsname); + cgconf_remove_backup_conffile(); + return -1; + } + + return 0; +} + +// 示例说明: +// 该函数用于处理类异常,首先检查指定的类是否存在,然后备份配置文件,接着根据不同的条件判断是否需要处理工作负载的异常。 +// 如果指定了异常中止标志位并且指定了工作负载名称,那么根据工作负载名称和类的关联关系找到对应的工作负载,并将其异常信息保存。 +// 如果没有指定工作负载名称,直接根据类的信息保存异常信息。 +// 如果指定的类不存在,则输出错误信息并返回异常。 + +// 语言块功能解析: +// 1. 备份配置文件:cgconf_backup_config_file()函数用于备份配置文件。 +// 2. 判断工作负载名称是否有级别或者没有级别:根据工作负载名称判断是否有级别,如果有则使用strcmp()函数进行比较,如果没有则使用strncmp()函数进行比较。 +// 3. 异常信息保存:cgexcp_exception_save()函数用于保存异常信息。 +// 4. 移除备份的配置文件:cgconf_remove_backup_conffile()函数用于移除备份的配置文件。 -- 2.34.1 From 40e0f4b53f86d1d5fcc52f292b2099daf9f105e6 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:01:39 +0800 Subject: [PATCH 05/56] Delete 'src/bin/gs_cgroup/cgexec.cpp' --- src/bin/gs_cgroup/cgexec.cpp | 5161 ---------------------------------- 1 file changed, 5161 deletions(-) delete mode 100644 src/bin/gs_cgroup/cgexec.cpp diff --git a/src/bin/gs_cgroup/cgexec.cpp b/src/bin/gs_cgroup/cgexec.cpp deleted file mode 100644 index 5554ac83f..000000000 --- a/src/bin/gs_cgroup/cgexec.cpp +++ /dev/null @@ -1,5161 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - *------------------------------------------------------------------------- - * - * cgexec.cpp - * Cgroup configration file process functions - * - * IDENTIFICATION - * src/bin/gs_cgroup/cgconf.cpp - * - *------------------------------------------------------------------------- - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "workload/gscgroup.h" - -#include "cgutil.h" -#include "securec.h" -#include "bin/elog.h" - -#ifdef ENABLE_UT -#define static -#endif - -#define MAX_COMMAND_LENGTH 128 -#define MOUNT_POINT_LENGTH (MAXPGPATH + 16) - -int cgutil_is_sles11_sp2 = 0; /* to indicate if the current OS is SLES SP2 version */ - -char* cgutil_subsys_table[] = { - MOUNT_CPU_NAME, MOUNT_CPUACCT_NAME, MOUNT_BLKIO_NAME, MOUNT_CPUSET_NAME, MOUNT_MEMORY_NAME}; - -static gscgroup_grp_t* cgutil_vaddr_back[GSCGROUP_ALLNUM] = {NULL}; /* for recovering */ - -/* - ***************** STATIC FUNCTIONS ************************ - */ -/* - * static functions for updating cpuset of different level of groups, - * declare here for use of functions that reset cpu cores of different level of groups. - */ -/*the core function of updating cpu cores. */ -static int cgexec_update_cgroup_cpuset_value(char* relpath, char* cpuset); -/* update class cpu cores and the belonging workload groups. */ -static int cgexec_update_class_cpuset(int cls, char* cpuset); -/* update top groups cpu cores and their all the belonging groups*/ -static int cgexec_update_top_group_cpuset(int top, char* cpuset); -/* update one group cpu cores */ -static int cgexec_update_cgroup_cpuset(gscgroup_grp_t* grp, char* cpuset); - -int CheckBackendEnv(const char* input_env_value) -{ - const int max_env_len = 1024; - const char* danger_character_list[] = {";", "`", "\\", "'", "\"", ">", "<", "$", "&", "|", "!", "\n", NULL}; - int i = 0; - - if (input_env_value == nullptr || strlen(input_env_value) >= max_env_len) { - fprintf(stderr, "ERROR: wrong environment variable \"%s\"\n", input_env_value); - return -1; - } - - for (i = 0; danger_character_list[i] != NULL; i++) { - if (strstr((const char*)input_env_value, danger_character_list[i])) { - fprintf(stderr, "ERROR: environment variable \"%s\" contain invaild symbol \"%s\".\n", - input_env_value, danger_character_list[i]); - return -1; - } - } - return 0; -} - -inline int CheckSystemSucess(pid_t status) -{ - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { - return 0; - } else { - fprintf(stderr, "command execute failed for: %d!\n", WEXITSTATUS(status)); - return -1; - } -} - -/* - * function name: cgexec_get_cgroup_number - * description : get the Cgroup numbers - * return value : - * -1: abnormal - * other: normal - */ -static int cgexec_get_cgroup_number(void) -{ - char buf[PROCLINE_LEN]; - FILE* f = NULL; - char *p = NULL, *q = NULL; - int hierarchy; - int cgcnt = -1; - - f = fopen("/proc/cgroups", "r"); - if (f == NULL) - return -1; - - while (NULL != fgets(buf, PROCLINE_LEN, f)) { - /* example from proc: - * #subsys_name hierarchy num_cgroups enabled - * cpu 0 1 1 - * - * get the first column, such as cpu - */ - p = buf; - q = strchr(p, '\t'); - if (q == NULL) - continue; - - *q = '\0'; - if (0 == strcmp(MOUNT_CPU_NAME, p)) { - while (*(q++) == ' ') - continue; - - /* get the second column */ - p = strchr(q, '\t'); - if (p == NULL) - break; - - *p = '\0'; - - hierarchy = (int)strtol(q, NULL, 10); - if (hierarchy == 0) { - fprintf(stderr, "cgroup is not mounted!\n"); - break; - } - - while (*(p++) == ' ') - continue; - - /* get the third column */ - q = strchr(p, '\t'); - if (q == NULL) { - fclose(f); - return -1; - } - *q = '\0'; - - cgcnt = (int)strtol(p, NULL, 10); - } - } - - fclose(f); - return cgcnt; -} - -/* - * @Description: check cpuset value. - * @IN clsset: class cpuset - * @IN grpset: group cpuset - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_check_cpuset_value(const char* clsset, const char* grpset) -{ - int clsstart, clsend; - int grpstart, grpend; - - errno_t ret = sscanf_s(clsset, "%d-%d", &clsstart, &clsend); - if (ret != 2) { - fprintf(stderr, - "%s:%d failed on calling " - "security function.\n", - __FILE__, - __LINE__); - return -1; - } - ret = sscanf_s(grpset, "%d-%d", &grpstart, &grpend); - if (ret != 2) { - fprintf(stderr, - "%s:%d failed on calling " - "security function.\n", - __FILE__, - __LINE__); - return -1; - } - - /* group cpuset value must be in class cpuset range */ - if (grpstart >= clsstart && grpend <= clsend) - return 0; - - return -1; -} - -/* - * @Description: get cpuset length. - * @IN cpuset: cpuset to be parsed - * @OUT start: start value of the cpuset - * @OUT end: end value of the cpuset - * @Return: length of the cpuset - * @See also: - */ -static int cgexec_get_cpuset_length(const char* cpuset, int* start, int* end) -{ - errno_t ret = sscanf_s(cpuset, "%d-%d", start, end); - if (ret != 2) { - fprintf(stderr, - "%s:%d failed on calling " - "security function.\n", - __FILE__, - __LINE__); - return -1; - } - - return *end - *start + 1; -} - -/* - * @Description: copy the start value and the end value into cpuset. - * @OUT cpuset: cpuset set well - * @IN start: start value of the cpuset - * @IN end: end value of the cpuset - * @See also: - */ -static void cgexec_get_cpu_core_range(char* cpuset, int start, int end) -{ - errno_t ret = sprintf_s(cpuset, CPUSET_LEN, "%d-%d", start, end); - securec_check_intval(ret, , ); -} -/* - * @Description : transfer from percentage to length of cpuset. - * @IN whole : the length of cpuset of the upper level group - * @IN wdpct : the percentage value of user set("--fixed"). - * @Return : -1: abnormal - * @Return : cpusetlength: the length of the cpuset to be updated. - * @See also: - */ -static int cgexec_trans_percent_to_cpusets(int whole, int wdpct) -{ - int cpusetlength = 0; - char tempvalue[CPUSET_LEN] = {0}; - char* temp = NULL; - errno_t ret; - - ret = sprintf_s(tempvalue, CPUSET_LEN, "%.1f", (float)whole * wdpct / GROUP_ALL_PERCENT); - securec_check_intval(ret, , -1); - - temp = strchr(tempvalue, '.'); - cpusetlength = atoi(tempvalue); - - if ((temp != NULL) && (*(++temp) > '5' || cpusetlength == 0)) { - cpusetlength++; - } - - return cpusetlength; -} - -/* - * @Description : transfer from length of cpuset to percentage. - * @IN highlen : the length of cpuset of the upper level group - * @IN lowlen : the percentage value of user set("--fixed"). - * @Return : cpusetlength: the length of the cpuset to be updated. - * @See also: - */ -static int cgexec_trans_cpusets_to_percent(int highlen, int lowlen) -{ - int pct = 0; - - if (highlen == 0) { - fprintf(stderr, "ERROR: %s:%d, Division by zero!\n", __FILE__, __LINE__); - return -1; - } - pct = lowlen * GROUP_ALL_PERCENT / highlen; - - /* - * if the number of cores of the system is lower than 100, - * we prefer the smaller percentage, to transfer the cpuset - * to quota as much as possible. - * - * make sure that the length got from the percentage - * will be the same with the low length. - */ - while (cgexec_trans_percent_to_cpusets(highlen, pct) < lowlen || !pct) - pct++; - - return pct; -} - -/* - * @Description : get cgroup id range - * @IN high : the id of the group id - * @OUT forstart : start group id of the range - * @OUT forend : end group id of the range - * @Return : -1: abnormal - * 0: the cpuset has been set well - * @See also : - */ -int cgexec_get_cgroup_id_range(int high, int* forstart, int* forend) -{ - if (high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) { - *forstart = WDCG_START_ID; - *forend = WDCG_END_ID; - } else if (high == TOPCG_CLASS) { - *forstart = CLASSCG_START_ID; - *forend = CLASSCG_END_ID; - } else if (high == TOPCG_BACKEND) { - *forstart = BACKENDCG_START_ID; - *forend = BACKENDCG_END_ID; - } else if (high == TOPCG_GAUSSDB) { - *forstart = TOPCG_BACKEND; - *forend = TOPCG_CLASS; - } else - return -1; - - return 0; -} -/* - * @Description : check whether the total percentage of the low groups - * are beyond the upper limit - * @IN high : the id of the high group that the low group belongs to. - * @IN low : the id of the low group to be updated. - * @OUT cpuset : if succeed, the calculated cpuset will be stored in it. - * @Return : -1: abnormal - * 0: the cpuset has been set well - * 1: need reset. - * @See also : - */ -static int cgexec_check_cpuset_percent(int high, int low, char* cpuset) -{ - /* start and end value of the loop */ - int forstart = 0, forend = 0; - /* start value and end values of the low and the high levels cpuset */ - int i, highstart = 0, highend = 0, lowstart = 0, lowend = 0; - /* sum of cpu cores and quota discarding the low groups which is to be updated */ - int sum_cpusets = 0, sum_quota = 0; - /* cpuset length of the groups and max value of the current low level groups */ - int lowlen = 0, highlen = 0, lowmax = 0; - /* return values which are to be restored in cpuset */ - int ret_start, ret_end; - - if (cgexec_get_cgroup_id_range(high, &forstart, &forend) == -1) - return -1; - - /* the cpuset length of the high level group */ - highlen = cgexec_get_cpuset_length(cgutil_vaddr[high]->cpuset, &highstart, &highend); - - /* the cpuset length to be updated. */ - lowlen = cgexec_trans_percent_to_cpusets(highlen, cgutil_opt.setspct); - - for (i = forstart; i <= forend; i++) { - /* the low level group is ignored currently */ - if (cgutil_vaddr[i]->used == 0 || i == low) - continue; - - /* only the workload groups with the same class "high" are considered. */ - if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && high != cgutil_vaddr[i]->ginfo.wd.cgid) - continue; - - /* - * in order to check whether the newly set setspct makes - * the total cpu cores out of range, count the sum of - * cpu cores, max core value, and the sum of quota value - * of the groups, ignoring the low group. - * - * quota is the percentage of cpu cores, - * if quota is 0, then the cpu cores would be set by default. - */ - if (cgutil_vaddr[i]->ainfo.quota) { - sum_cpusets += cgexec_get_cpuset_length(cgutil_vaddr[i]->cpuset, &lowstart, &lowend); - sum_quota += cgutil_vaddr[i]->ainfo.quota; - lowmax = (lowend > lowmax) ? lowend : lowmax; - } - } - /* - * if sum of quota values and the newly set setspct out of range, - * an error is thrown out. However, there are some cases, that the - * quota is not out of range, but sum of cpu cores are, since the - * calculated decimals (such as 1.6 is rounded to 2, and 0.1 is - * rounded to 1, 1.5 is rounded to 1) are rounded up or down. - * these cases will be handled in macro GET_CPUSET_START_VALUE. - * For example, if there are 2 cores left for the newly set group, - * but it need 3 cores after calculation from setspct, - * then it will be set the last three cores. - */ - if (sum_quota + cgutil_opt.setspct > GROUP_ALL_PERCENT) { - if (*cgutil_vaddr[low]->grpname) - fprintf(stderr, - "ERROR: the total percentage of cpu cores are larger than 100, " - "you cannot set %d%% for group \"%s\"\n", - cgutil_opt.setspct, - cgutil_vaddr[low]->grpname); - - return -1; - } - - ret_start = GET_CPUSET_START_VALUE(highstart, highend, sum_cpusets, lowmax, lowlen); - ret_end = ret_start + lowlen - 1; - cgexec_get_cpu_core_range(cpuset, ret_start, ret_end); - - /* return value indicates need reset or not */ - return (ret_start > lowmax) ? 0 : 1; -} -/* - * @Description : reset group cpuset values. - * @IN high : high level group id that low group belongs to - * @IN low : low level group to be updated, which is not included in the reseting list. - * @Return -1 : abnormal - * @Return 0 : normal. - * @See also: - */ -static int cgexec_reset_cpuset_cgroups(int high, int low) -{ - int forstart = 0, forend = 0; /* start and end value of the loop */ - int i = 0; - int lowlen = 0, highlen = 0; /* low and high level cpuset length */ - int lowstart = 0, lowend = 0; /* low group cpuset start and end value */ - int highstart = 0, highend = 0; /* high group cpuset start and end value */ - char sets[CPUSET_LEN]; /* the calculated cpuset to be updated */ - bool flag = false; /*flag to indicate first time enter the loop */ - - if (cgexec_get_cgroup_id_range(high, &forstart, &forend) == -1) - return -1; - - /* the cpuset length of the high level group */ - highlen = cgexec_get_cpuset_length(cgutil_vaddr[high]->cpuset, &highstart, &highend); - - for (i = forstart; i <= forend; i++) { - /* the low level group is ignored in the reseting list */ - if (cgutil_vaddr[i]->used == 0 || (low != 0 && i == low)) - continue; - - /* only the workload groups belonging to high class is considered */ - if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && high != cgutil_vaddr[i]->ginfo.wd.cgid) - continue; - - /* only groups with quota values are considered */ - if (cgutil_vaddr[i]->ainfo.quota) { - /* the low level groups (same level groups with "low") cpu core length */ - lowlen = cgexec_trans_percent_to_cpusets(highlen, cgutil_vaddr[i]->ainfo.quota); - - /* - * only the first time enter the loop, flag is false - * the cpu cores are allocated sequentially from high group cpu core range. - * the first group to be reset is allocated from "highstart", - * the next are allocated following the previous group "lowend" + 1 - */ - lowstart = flag ? (lowend + 1) : highstart; - lowend = lowstart + lowlen - 1; - - /* - * callers of this function can guarantee the total quota not out of range, - * so here we only need check whether the left cpu cores are enough or not, - * and and the not enough cases will be handled in the same way with - * cgexec_check_cpuset_percent. - */ - if (lowend > highend) { - lowstart = highend - lowlen + 1; - lowend = highend; - } - /* "sets" restore the cpuset to be reset*/ - cgexec_get_cpu_core_range(sets, lowstart, lowend); - - /* reset the group cpuset with "sets" */ - if ((high == TOPCG_CLASS && cgexec_update_class_cpuset(i, sets) == -1) || - (high == TOPCG_GAUSSDB && cgexec_update_top_group_cpuset(i, sets) == -1) || - (((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) || high == TOPCG_BACKEND) && - (cgexec_update_cgroup_cpuset(cgutil_vaddr[i], sets) == -1))) { - fprintf(stderr, "ERROR: reset cpu cores for \"%s\" failed\n", cgutil_vaddr[i]->grpname); - return -1; - } - - /* next time enter the loop, flag will be true*/ - if (!flag) - flag = true; - } - /* - * for the case of reseting backend groups, or workload groups, since they don't have - * low level groups, no reset is needed. - * in other cases, reseting the low level groups recursively is needed. - */ - if ((high == TOPCG_CLASS || high == TOPCG_GAUSSDB) && cgexec_reset_cpuset_cgroups(i, 0) == -1) { - fprintf( - stderr, "ERROR: reset group failed when reseting cpuset for group \"%s\".\n", cgutil_vaddr[i]->grpname); - return -1; - } - } - return 0; -} - -/* - * @Description : get the total cpu core percentage of the groups, - * with "high" as their higher level group id - * @IN high : high level group id. - * @Return : total quota value - * @See also: - */ -static int cgexec_check_fixed_percent(int high) -{ - int forstart = 0, forend = 0; /* start and end value of the loop */ - int sets_total_pct = 0; /* total percentage of the low groups */ - int i = 0; - - (void)cgexec_get_cgroup_id_range(high, &forstart, &forend); - - for (i = forstart; i <= forend; i++) { - if (cgutil_vaddr[i]->used == 0 || !cgutil_vaddr[i]->ainfo.quota) - continue; - - /* only the workload groups belonging to high class is considered */ - if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && high != cgutil_vaddr[i]->ginfo.wd.cgid) - continue; - - sets_total_pct += cgutil_vaddr[i]->ainfo.quota; - } - - return sets_total_pct; -} - -/* - * @Description: get large cpuset value. - * @IN clsset: class cpuset - * @IN grpset: group cpuset - * @OUT result: large cpuset - * @Return: large cpuset value - * @See also: - */ -char* cgexec_get_large_cupset(const char* clsset, const char* grpset, char* result) -{ - int clsstart, clsend; - int grpstart, grpend; - int resstart, resend; - - int rc = sscanf_s(clsset, "%d-%d", &clsstart, &clsend); - if (rc != 2) { - fprintf(stderr, - "%s:%d failed on calling " - "security function.\n", - __FILE__, - __LINE__); - return NULL; - } - - rc = sscanf_s(grpset, "%d-%d", &grpstart, &grpend); - if (rc != 2) { - fprintf(stderr, - "%s:%d failed on calling " - "security function.\n", - __FILE__, - __LINE__); - return NULL; - } - - /* get large start value */ - resstart = (clsstart < grpstart) ? clsstart : grpstart; - /* get large end value */ - resend = (clsend > grpend) ? clsend : grpend; - - /* get large cpuset */ - rc = sprintf_s(result, CPUSET_LEN, "%d-%d", resstart, resend); - /* check the return value of security function */ - securec_check_ss_c(rc, "\0", "\0"); - - return result; -} - -/* - * @Description: get cgroup info with relpath. - * @IN relpath: relpath of the cgroup - * @Return: cgroup info - * @See also: - */ -struct cgroup* cgexec_get_cgroup(const char* relpath) -{ - struct cgroup* cg = NULL; - int ret; - - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); - return NULL; - } - - /* get all information regarding the cgroup from kernel */ - ret = cgroup_get_cgroup(cg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to get cgroup information for %s(%d)\n", cgroup_strerror(ret), ret); - cgroup_free(&cg); - return NULL; - } - - return cg; -} - -/* - * function name: cgexec_update_remain_value - * description : update the dynamic value of Remain Cgroup - * arguments : - * relpath: the relative path of Remain Cgroup - * cpushares: the value of cpu.shares - * ioweight: the value of blkio.weight - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function only updates the cpu.shares and blkio.weight. - */ -int cgexec_update_remain_value(char* relpath, u_int64_t cpushares, u_int64_t ioweight) -{ - struct cgroup* cg = NULL; - struct cgroup_controller* cgc_cpu = NULL; - int ret; - - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - ret = ECGFAIL; - fprintf(stdout, "ERROR: failed to create the new %s cgroup for %s\n", relpath, cgroup_strerror(ret)); - return -1; - } - - /* get all information regarding the cgroup from kernel */ - ret = cgroup_get_cgroup(cg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); - cgroup_free(&cg); - return -1; - } - - /* get the cpu controller */ - cgc_cpu = cgroup_get_controller(cg, MOUNT_CPU_NAME); - if (NULL == cgc_cpu) { - fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPU_NAME, relpath); - cgroup_free(&cg); - return -1; - } - - if (cpushares && (0 != (ret = cgroup_set_value_uint64(cgc_cpu, CPU_SHARES, cpushares)))) { - fprintf(stderr, "ERROR: failed to set %s as %lu for %s\n", CPU_SHARES, cpushares, cgroup_strerror(ret)); - cgroup_free_controllers(cg); - cgroup_free(&cg); - return -1; - } - - /* update controller into kernel */ - if (0 != (ret = cgroup_modify_cgroup(cg))) { - fprintf(stderr, - "ERROR: failed to modify cgroup for %s " - "when modifying values!\n", - cgroup_strerror(ret)); - cgroup_free_controllers(cg); - cgroup_free(&cg); - return -1; - } - - cgroup_free_controllers(cg); - cgroup_free(&cg); - - return 0; -} - -/* - * function name: cgexec_update_remain_cgroup - * description : get the value of Remain Cgroup and - * update them into kernel cgroup - * arguments : - * grp: the configuration information of workload group - * which has the same level as Remain Cgroup - * cls: the group ID of Class which has the workload group - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function is used when updating value of workload group. - */ -static int cgexec_update_remain_cgroup(gscgroup_grp_t* grp, int cls) -{ - char* relpath = NULL; - int i, j, ret, rempct = GROUP_ALL_PERCENT; - char rempath[16]; - u_int64_t cpushares, ioweight; - errno_t sret; - - /* - * calculate the remain percent of group - * whose level is larger than specified workload group - * count from 2 is for discarding the TopWD group - */ - for (i = 2; i < grp->ginfo.wd.wdlevel; i++) { - /* calculate the remain percentage */ - for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { - if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cls && - cgutil_vaddr[j]->ginfo.wd.wdlevel == i) - break; - } - - rempct -= cgutil_vaddr[j]->ginfo.wd.percent; - } - - /* update the workload whose level is larger than specified workload */ - for (i = grp->ginfo.wd.wdlevel; i <= cgutil_vaddr[cls]->ginfo.cls.maxlevel; i++) { - for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { - if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cls && - cgutil_vaddr[j]->ginfo.wd.wdlevel == i) - break; - } - - /* get the parent path of the workload group */ - relpath = gscgroup_get_parent_wdcg_path(j, cgutil_vaddr, current_nodegroup); - if (NULL == relpath) - return -1; - - /* get the remain group path */ - sret = snprintf_s(rempath, - sizeof(rempath), - sizeof(rempath) - 1, - "%s:%d/", - GSCGROUP_REMAIN_WORKLOAD, - cgutil_vaddr[j]->ginfo.wd.wdlevel); - securec_check_intval(sret, free(relpath), -1); - - sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); - securec_check_errno(sret, free(relpath), -1); - - /* update the remain cgroup */ - rempct -= cgutil_vaddr[j]->ginfo.wd.percent; - - cpushares = (u_int64_t)MAX_CLASS_CPUSHARES * rempct / GROUP_ALL_PERCENT; - ioweight = (u_int64_t)IO_WEIGHT_CALC(MAX_IO_WEIGHT, rempct); - - ret = cgexec_update_remain_value(relpath, cpushares, ioweight); - if (-1 == ret) { - free(relpath); - relpath = NULL; - return -1; - } - - free(relpath); - relpath = NULL; - } - - return 0; -} - -/* - * function name: cgexec_update_cgroup_value - * description : update the Cgroup information - * based on the value of group configuration information. - * arguments : - * grp: the configuration information of group - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function is used when updating dynamic value and fiexed value. - */ -static int cgexec_update_cgroup_value(gscgroup_grp_t* grp) -{ - char* relpath = NULL; - struct cgroup* cg = NULL; - long cpushares = grp->ainfo.shares; - struct cgroup_controller* cgc_cpu = NULL; - int ret; - - /* get the relative path */ - if (NULL == (relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup))) - return -1; - - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); - free(relpath); - relpath = NULL; - return -1; - } - - /* get all information regarding the cgroup from kernel */ - ret = cgroup_get_cgroup(cg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); - free(relpath); - relpath = NULL; - cgroup_free(&cg); - return -1; - } - - /* get the CPU controller */ - cgc_cpu = cgroup_get_controller(cg, MOUNT_CPU_NAME); - if (NULL == cgc_cpu) { - fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPU_NAME, grp->grpname); - free(relpath); - relpath = NULL; - cgroup_free(&cg); - return -1; - } - - /* when it is dynamic value, it updates the cpu.shares value */ - if (0 == cgutil_opt.fixed || cgutil_opt.recover) { - if (cpushares && (0 != (ret = cgroup_set_value_uint64(cgc_cpu, CPU_SHARES, cpushares)))) { - fprintf(stderr, "ERROR: failed to set %s as %ld for %s\n", CPU_SHARES, cpushares, cgroup_strerror(ret)); - goto error; - } - } - - /* when it is recovering the group, it update the cpuset value in here */ - if (cgutil_opt.recover && grp->cpuset[0]) { - /* get the CPUSET controller */ - struct cgroup_controller* cgc_cpus = cgroup_get_controller(cg, MOUNT_CPUSET_NAME); - if (NULL == cgc_cpus) { - fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); - goto error; - } - - /* get cpuset value with controller */ - if (0 != (ret = cgroup_set_value_string(cgc_cpus, CPUSET_CPUS, grp->cpuset))) { - fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, grp->cpuset, cgroup_strerror(ret)); - goto error; - } - } - - /* modify the value into kernel */ - if (0 != (ret = cgroup_modify_cgroup(cg))) { - fprintf(stderr, - "ERROR: failed to modify cgroup for %s " - "when updating %s group!\n", - cgroup_strerror(ret), - grp->grpname); - goto error; - } - - cgroup_free_controllers(cg); - cgroup_free(&cg); - - free(relpath); - relpath = NULL; - return 0; - -error: - cgroup_free_controllers(cg); - cgroup_free(&cg); - free(relpath); - relpath = NULL; - return -1; -} - -/* - * @Description: search workload group id with class id. - * @IN cls: class id - * @Return: workload group id - * @See also: - */ -static int cgexec_search_workload_group(int cls) -{ - int i, wd = 0, cmp = -1; - char* tmpstr = strchr(cgutil_opt.wdname, ':'); - size_t wdname_len = strlen(cgutil_opt.wdname); - - /* search workload group */ - for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { - if (cgutil_vaddr[i]->used == 0 || cgutil_vaddr[i]->ginfo.wd.cgid != cls) - continue; - - /* workload name with level or no level */ - if (tmpstr != NULL) - cmp = strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname); - else { - if (':' == cgutil_vaddr[i]->grpname[wdname_len]) - cmp = strncmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname, wdname_len); - } - - if (0 == cmp) { - wd = i; - break; - } - } - - return wd; -} - -/* - * function name: cgexec_create_default_cgroup - * description : create a cgroup on the specified path based on the values - * arguments : - * relpath: the relative path of Cgroup - * cpushares: the value of cpu.shares - * ioweight: the value of blkio.weight - * cpuset : the value of cpu.cpus - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function is used when creating new Cgroup. - */ -static int cgexec_create_default_cgroup(char* relpath, int cpushares, int ioweight, char* cpuset) -{ - int ret; - struct cgroup* cg = NULL; - struct cgroup_controller* cgc_cpu = NULL; - struct cgroup_controller* cgc_cpuset = NULL; - struct cgroup_controller* cgc_cpuacct = NULL; - - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - fprintf(stdout, "failed to create the new cgroup for %s\n", relpath); - return -1; - } - - /* set the uid and gid */ - ret = cgroup_set_uid_gid(cg, - cgutil_passwd_user->pw_uid, - cgutil_passwd_user->pw_gid, - cgutil_passwd_user->pw_uid, - cgutil_passwd_user->pw_gid); - if (ret) { - fprintf(stderr, "ERROR: failed to set uid and gid for %s!\n", cgroup_strerror(ret)); - cgroup_free(&cg); - return -1; - } - - /* add the controller */ - cgc_cpu = cgroup_add_controller(cg, MOUNT_CPU_NAME); - if (NULL == cgc_cpu) { - fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPU_NAME, relpath); - cgroup_free(&cg); - return -1; - } - - /* set the cpu.shares value */ - if (cpushares && (0 != (ret = cgroup_set_value_uint64(cgc_cpu, CPU_SHARES, cpushares)))) { - fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPU_SHARES, cpushares, cgroup_strerror(ret)); - - goto error; - } - - /* set the cpuset.cpus value */ - cgc_cpuset = cgroup_add_controller(cg, MOUNT_CPUSET_NAME); - if (NULL == cgc_cpuset) { - fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUSET_NAME, relpath); - goto error; - } - - if (*cpuset) { - /* set the cpuset.mems value */ - if (0 != (ret = cgroup_set_value_string(cgc_cpuset, CPUSET_MEMS, cgutil_mems))) { - fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUSET_MEMS, 0, cgroup_strerror(ret)); - goto error; - } - - /* set the cpuset.cpus value */ - if (0 != (ret = cgroup_set_value_string(cgc_cpuset, CPUSET_CPUS, cpuset))) { - fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, cpuset, cgroup_strerror(ret)); - goto error; - } - } - - /* add the controller */ - cgc_cpuacct = cgroup_add_controller(cg, MOUNT_CPUACCT_NAME); - if (NULL == cgc_cpuacct) { - fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUACCT_NAME, relpath); - goto error; - } - - /* set the cpu.usage value */ - if (0 != (ret = cgroup_set_value_uint64(cgc_cpuacct, CPUACCT_USAGE, 0))) { - fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUACCT_USAGE, 0, cgroup_strerror(ret)); - - goto error; - } - - /* create the Cgroup on kernel */ - ret = cgroup_create_cgroup(cg, 0); - if (ret) { - fprintf(stderr, "ERROR: can't create cgroup for %s\n", cgroup_strerror(ret)); - goto error; - } - - cgroup_free_controllers(cg); - cgroup_free(&cg); - - return 0; - -error: - cgroup_free_controllers(cg); - cgroup_free(&cg); - return -1; -} - -/* - * function name: cgexec_create_remain_cgroup - * description : create the remain cgroup based on the same level workload group - * arguments : - * grp: the workload group - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function is used when creating new Cgroup. - */ -static int cgexec_create_remain_cgroup(gscgroup_grp_t* grp) -{ - char* relpath = NULL; - long cpushares; - long ioweight; - int i, changed = 0; - char rempath[16]; - errno_t sret; - - if (NULL == (relpath = gscgroup_get_parent_wdcg_path(grp->gid, cgutil_vaddr, current_nodegroup))) - return -1; - - /* add the remain path dir */ - sret = snprintf_s( - rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d", GSCGROUP_REMAIN_WORKLOAD, grp->ginfo.wd.wdlevel); - securec_check_intval(sret, free(relpath), -1); - - sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); - securec_check_errno(sret, free(relpath), -1); - - /* get the class group */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->gid == grp->ginfo.wd.cgid) - break; - } - - if (i > CLASSCG_END_ID) { - free(relpath); - relpath = NULL; - return -1; - } - - if (grp->ginfo.cls.maxlevel == 1 && GROUP_ALL_PERCENT == cgutil_vaddr[i]->ginfo.cls.rempct) { - changed = 1; - cgutil_vaddr[i]->ginfo.cls.rempct = NORMALWD_PERCENT; - } - - cpushares = MAX_CLASS_CPUSHARES * cgutil_vaddr[i]->ginfo.cls.rempct / GROUP_ALL_PERCENT; - ioweight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_vaddr[i]->ginfo.cls.rempct); - - sret = - snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[grp->ginfo.wd.cgid]->cpuset); - securec_check_intval(sret, free(relpath), -1); - - (void)cgexec_create_default_cgroup(relpath, cpushares, ioweight, cgutil_vaddr[i]->cpuset); - - if (changed) - cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; - - free(relpath); - relpath = NULL; - - return 0; -} - -/* - * function name: cgexec_set_blkio_throttle_value - * description : set the blkio throttle value when creating new cgroup - * : based on configure file - */ -int cgexec_set_blkio_throttle_value(const char* relpath, const char* name, const char* value) -{ - int ret; - char *p = NULL, *q = NULL, *head = NULL, *i = NULL; - struct cgroup* cg = NULL; - struct cgroup_controller* cgc = NULL; - - /* allocate new cgroup structure */ - if ((cg = cgexec_get_cgroup(relpath)) == NULL) - return -1; - - /* get controller */ - cgc = cgroup_get_controller(cg, MOUNT_BLKIO_NAME); - if (cgc == NULL) { - cgroup_free(&cg); - return -1; - } - - head = strdup(value); - if (head == NULL) { - cgroup_free_controllers(cg); - cgroup_free(&cg); - return -1; - } - p = head; - - do { - q = strchr(p, '\n'); - if (q != NULL) - *q++ = '\0'; - - i = p; - while (*i++) { - if (*i == '\t') - *i = ' '; - } - - ret = cgroup_set_value_string(cgc, name, p); - if (ret) { - fprintf(stderr, "failed to set %s as %s for %s\n", name, p, cgroup_strerror(ret)); - p = q; - continue; - } - - /* update controller into kernel */ - if (0 != (ret = cgroup_modify_cgroup(cg))) { - fprintf(stderr, - "failed to modify cgroup for %s " - "when modifying values!\n", - cgroup_strerror(ret)); - p = q; - continue; - } - - p = q; - } while (q != NULL); - - free(head); - head = NULL; - cgroup_free_controllers(cg); - cgroup_free(&cg); - - return 0; -} - -/* - * function name: cgexec_create_new_cgroup - * description : create the new Cgroup based on configuration information - * arguments : - * grp: the configuration information - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function is used when creating new Cgroup. - */ -int cgexec_create_new_cgroup(gscgroup_grp_t* grp) -{ - char* relpath = NULL; - int ret; - struct cgroup* cg = NULL; - struct cgroup_controller* cg_controllers[MOUNT_SUBSYS_KINDS] = {0}; - - if (NULL == (relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup))) - return -1; - - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - fprintf(stdout, "failed to create the new cgroup for %s\n", relpath); - free(relpath); - relpath = NULL; - return -1; - } - - /* set the uid and gid */ - ret = cgroup_set_uid_gid(cg, - cgutil_passwd_user->pw_uid, - cgutil_passwd_user->pw_gid, - cgutil_passwd_user->pw_uid, - cgutil_passwd_user->pw_gid); - if (ret) { - fprintf(stderr, "ERROR: failed to set uid and gid for %s!\n", cgroup_strerror(ret)); - free(relpath); - relpath = NULL; - cgroup_free(&cg); - return -1; - } - - /* add the controller */ - cg_controllers[MOUNT_CPU_ID] = cgroup_add_controller(cg, MOUNT_CPU_NAME); - if (NULL == cg_controllers[MOUNT_CPU_ID]) { - fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPU_NAME, grp->grpname); - free(relpath); - relpath = NULL; - cgroup_free(&cg); - return -1; - } - - /* set the cpu.shares value */ - if (grp->ainfo.shares && - (0 != (ret = cgroup_set_value_uint64(cg_controllers[MOUNT_CPU_ID], CPU_SHARES, grp->ainfo.shares)))) { - fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPU_SHARES, grp->ainfo.shares, cgroup_strerror(ret)); - goto error; - } - - cg_controllers[MOUNT_CPUSET_ID] = cgroup_add_controller(cg, MOUNT_CPUSET_NAME); - if (NULL == cg_controllers[MOUNT_CPUSET_ID]) { - fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUSET_NAME, grp->grpname); - goto error; - } - - /* set the cpu.cpus value */ - if (*grp->cpuset) { - if ((0 != (ret = cgroup_set_value_string(cg_controllers[MOUNT_CPUSET_ID], CPUSET_MEMS, cgutil_mems)))) { - fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUSET_MEMS, 0, cgroup_strerror(ret)); - goto error; - } - - if (0 != (ret = cgroup_set_value_string(cg_controllers[MOUNT_CPUSET_ID], CPUSET_CPUS, grp->cpuset))) { - fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, grp->cpuset, cgroup_strerror(ret)); - goto error; - } - } - /* add cpuacct controllor */ - cg_controllers[MOUNT_CPUACCT_ID] = cgroup_add_controller(cg, MOUNT_CPUACCT_NAME); - if (NULL == cg_controllers[MOUNT_CPUACCT_ID]) { - fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUACCT_NAME, grp->grpname); - goto error; - } - - if (0 != (ret = cgroup_set_value_int64(cg_controllers[MOUNT_CPUACCT_ID], CPUACCT_USAGE, 0))) { - fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUACCT_USAGE, 0, cgroup_strerror(ret)); - goto error; - } - - /* create the Cgroup on kernel */ - ret = cgroup_create_cgroup(cg, 0); - if (ret) { - fprintf(stderr, "ERROR: can't create cgroup for %s\n", cgroup_strerror(ret)); - goto error; - } - - cgroup_free_controllers(cg); - cgroup_free(&cg); - - free(relpath); - relpath = NULL; - return 0; - -error: - free(relpath); - relpath = NULL; - cgroup_free_controllers(cg); - cgroup_free(&cg); - return -1; -} - -/* - * function name: cgexec_create_workload_cgroup - * description : create the new Cgroup based on configuration information - * arguments : - * grp: the configuration information of workload group - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function is used when creating new workload Cgroup. - */ -static int cgexec_create_workload_cgroup(gscgroup_grp_t* grp) -{ - int cgid = grp->ginfo.wd.cgid; - gscgroup_grp_t* cls_grp = cgutil_vaddr[cgid]; - int nextlevel = cls_grp->ginfo.cls.maxlevel + 1; - int i, j; - - /* skip the workload */ - if (nextlevel > grp->ginfo.wd.wdlevel) - return 0; - /* when the workload is the next level workload group */ - if (nextlevel == grp->ginfo.wd.wdlevel) { - if (-1 == cgexec_create_new_cgroup(grp)) - return -1; - - if (-1 == cgexec_create_remain_cgroup(grp)) - return -1; - } - /* need to create all parent workload group firstly */ - else if (nextlevel < grp->ginfo.wd.wdlevel) { - cls_grp->ginfo.cls.rempct += grp->ginfo.wd.percent; - - for (i = nextlevel; i < grp->ginfo.wd.wdlevel; i++) { - for (j = grp->gid; j <= WDCG_END_ID; j++) { - if (cgutil_vaddr[j]->used && cgid == cgutil_vaddr[j]->ginfo.wd.cgid && - i == cgutil_vaddr[j]->ginfo.wd.wdlevel) - break; - } - - if (j > WDCG_END_ID) { - fprintf(stderr, "can't find the parent workload!\n"); - return -1; - } - - if (-1 == cgexec_create_new_cgroup(cgutil_vaddr[j])) - return -1; - - cls_grp->ginfo.cls.rempct -= cgutil_vaddr[j]->ginfo.wd.percent; - - if (-1 == cgexec_create_remain_cgroup(cgutil_vaddr[j])) - return -1; - - /* set the maxlevel value of class group */ - cls_grp->ginfo.cls.maxlevel = i; - } - - if (-1 == cgexec_create_new_cgroup(grp)) - return -1; - - cls_grp->ginfo.cls.rempct -= grp->ginfo.wd.percent; - - if (-1 == cgexec_create_remain_cgroup(grp)) - return -1; - } - - /* set the maxlevel of Class group */ - cgutil_vaddr[cgid]->ginfo.cls.maxlevel += 1; - - return 0; -} - -/* - * function name: cgexec_create_timeshare_cgroup - * description : create the all timeshare Cgroup of the specified Class Cgroup - * arguments : - * grp: the configuration information of Class group - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function is used when creating new Class Cgroup. - */ -static int cgexec_create_timeshare_cgroup(gscgroup_grp_t* grp) -{ - char* toppath = NULL; - char* relpath = NULL; - long cpushares; - long ioweight; - int j, ret; - - /* create the top timeshare cgroup */ - cpushares = DEFAULT_CPU_SHARES; - ioweight = DEFAULT_IO_WEIGHT; - - /* get the top timeshare path */ - toppath = gscgroup_get_topts_path(grp->gid, cgutil_vaddr, current_nodegroup); - if (NULL == toppath) - return -1; - - /* create the top level Cgroup */ - ret = cgexec_create_default_cgroup(toppath, cpushares, ioweight, grp->cpuset); - if (-1 == ret) { - free(toppath); - toppath = NULL; - return -1; - } - - /* allocate memory for path of timeshare cgroup */ - if (NULL == (relpath = (char*)malloc(GPNAME_PATH_LEN))) { - fprintf(stderr, "ERROR: failed to allocate memory for path!\n"); - free(toppath); - toppath = NULL; - return -1; - } - - /* create the default timeshare cgroups */ - for (j = TSCG_START_ID; j <= TSCG_END_ID; j++) { - int rc = snprintf_s(relpath, GPNAME_PATH_LEN, GPNAME_PATH_LEN - 1, "%s/%s", toppath, cgutil_vaddr[j]->grpname); - securec_check_intval(rc, free(toppath); free(relpath), -1); - - cpushares = cgutil_vaddr[j]->ainfo.shares; - ioweight = cgutil_vaddr[j]->ainfo.weight; - - ret = cgexec_create_default_cgroup(relpath, cpushares, ioweight, grp->cpuset); - if (-1 == ret) { - free(toppath); - toppath = NULL; - free(relpath); - relpath = NULL; - return -1; - } - } - - free(toppath); - toppath = NULL; - free(relpath); - relpath = NULL; - - return 0; -} - -/* - * function name: cgexec_delete_default_cgroup - * description : delete the Cgroup based on configuration information - * arguments : - * grp: the Group configuration information - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function is used when dropping a Cgroup. - */ -static int cgexec_delete_default_cgroup(gscgroup_grp_t* grp) -{ - char* relpath = NULL; - - relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup); - if (NULL == relpath) - return -1; - - (void)cgexec_delete_cgroups(relpath); - - free(relpath); - relpath = NULL; - - return 0; -} - -/* - * function name: cgexec_create_nodegroup_default_cgroups - * description : create default cgroups based on node group name - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_create_nodegroup_default_cgroups(void) -{ - int i, ret = 0; - errno_t sret; - char* cpuset = NULL; - char cpu_allset[CPUSET_LEN] = {0}; - - /* get memory set */ - if (-1 == cgexec_get_cgroup_cpuset_info(TOPCG_GAUSSDB, &cpuset)) { - fprintf(stderr, "ERROR: failed to get cpusets and mems during creating default nodegroup cgroups.\n"); - return -1; - } - - /* set gaussdb default cpuset value */ - if ((cpuset != NULL) && *cpuset != '\0') { - sret = snprintf_s(cpu_allset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); - securec_check_intval(sret, free(cpuset), -1); - free(cpuset); - cpuset = NULL; - } else { - sret = snprintf_s(cpu_allset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); - securec_check_intval(sret, , -1); - } - - sret = snprintf_s(cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpu_allset); - securec_check_intval(sret, , -1); - - /* create nodegroup cgroup */ - if (-1 == (ret = cgexec_create_new_cgroup(cgutil_vaddr[TOPCG_CLASS]))) { - fprintf(stderr, "failed to create %s cgroup!\n", cgutil_vaddr[TOPCG_CLASS]->grpname); - return -1; - } - - /* create all Cgroup except the timeshare Cgroup */ - for (i = CLASSCG_START_ID; i <= WDCG_END_ID; i++) { - if (0 == cgutil_vaddr[i]->used) - continue; - - /* update the cpuset info */ - sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpu_allset); - securec_check_intval(sret, , -1); - - if (i >= CLASSCG_START_ID && i <= CLASSCG_END_ID) { - /* reset the maxlevel number */ - cgutil_vaddr[i]->ginfo.cls.maxlevel = 0; - cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; - - ret = cgexec_create_new_cgroup(cgutil_vaddr[i]); - } else if (i >= WDCG_START_ID && i <= WDCG_END_ID) { - int cls = cgutil_vaddr[i]->ginfo.wd.cgid; - - if (strncmp(cgutil_vaddr[i]->grpname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1) == 0) - cgconf_set_top_workload_group(i, cls); - - if (cgutil_vaddr[i]->ginfo.cls.maxlevel == 1) - cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; - else if (cgutil_vaddr[cls]->ginfo.cls.maxlevel < cgutil_vaddr[i]->ginfo.wd.wdlevel) - cgutil_vaddr[cls]->ginfo.cls.rempct -= cgutil_vaddr[i]->ginfo.cls.percent; - - ret = cgexec_create_workload_cgroup(cgutil_vaddr[i]); - } - - if (-1 == ret) { - fprintf(stderr, "failed to create %s cgroup!\n", cgutil_vaddr[i]->grpname); - continue; - } - } - - /* create the timeshare group of each Class group */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (0 == cgutil_vaddr[i]->used) - continue; - - ret = cgexec_create_timeshare_cgroup(cgutil_vaddr[i]); - if (-1 == ret) { - fprintf(stderr, "failed to create timeshare cgroup for %s!\n", cgutil_vaddr[i]->grpname); - return -1; - } - } - - return ret; -} - -/* - * function name: cgexec_create_default_cgroups - * description : when there is no Cgroups on kernel, it means that it need - * to create the Cgroups based on the default Configuration file. - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_create_default_cgroups(void) -{ - int i, ret = 0; - errno_t sret; - - /* the root Cgroup has exists after mounting Cgroup file system */ - if ((cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) && (cgutil_opt.refresh == 0 && cgutil_opt.revert == 0)) - (void)cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_ROOT]); - - /* create all Cgroup except the timeshare Cgroup */ - for (i = 1; i <= WDCG_END_ID; i++) { - if (0 == cgutil_vaddr[i]->used) - continue; - - /* set gaussdb default cpuset value */ - if (*cgutil_vaddr[TOPCG_GAUSSDB]->cpuset == '\0') { - sret = snprintf_s(cgutil_vaddr[TOPCG_GAUSSDB]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); - securec_check_intval(sret, , -1); - } - - /* Top Cgroup */ - // how to process quota and cpuset? - // 1. if Gaussdb range is changed, all subdir's quota should be changed - // so cgexec_check_top_cpuset the function is not enough to process this - // 2. if cpusets is not the same as upper dir, it should caclucate the quota value - - if (i > TOPCG_GAUSSDB && *cgutil_vaddr[i]->cpuset == '\0') { - sret = snprintf_s( - cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); - securec_check_intval(sret, , -1); - } - - if (i < CLASSCG_START_ID) - ret = cgexec_create_new_cgroup(cgutil_vaddr[i]); - else if (i >= CLASSCG_START_ID && i <= CLASSCG_END_ID) { - if (0 == cgutil_vaddr[i]->used) - continue; - - /* reset the maxlevel number */ - cgutil_vaddr[i]->ginfo.cls.maxlevel = 0; - cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; - - ret = cgexec_create_new_cgroup(cgutil_vaddr[i]); - } else if (i >= WDCG_START_ID && i <= WDCG_END_ID) { - int cls = cgutil_vaddr[i]->ginfo.wd.cgid; - - if (strncmp(cgutil_vaddr[i]->grpname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1) == 0) - cgconf_set_top_workload_group(i, cls); - - if (cgutil_vaddr[i]->ginfo.cls.maxlevel == 1) - cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; - else if (cgutil_vaddr[cls]->ginfo.cls.maxlevel < cgutil_vaddr[i]->ginfo.wd.wdlevel) - cgutil_vaddr[cls]->ginfo.cls.rempct -= cgutil_vaddr[i]->ginfo.cls.percent; - - ret = cgexec_create_workload_cgroup(cgutil_vaddr[i]); - } - - if (-1 == ret) { - fprintf(stderr, "failed to create %s cgroup!\n", cgutil_vaddr[i]->grpname); - continue; - } - } - - /* create the timeshare group of each Class group */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (0 == cgutil_vaddr[i]->used) - continue; - - ret = cgexec_create_timeshare_cgroup(cgutil_vaddr[i]); - if (-1 == ret) { - fprintf(stderr, "failed to create timeshare cgroup for %s!\n", cgutil_vaddr[i]->grpname); - return -1; - } - } - - return ret; -} - -/* - * function name: cgexec_is_same_group - * description : check whether old workload group and the new group is the same. - * return value : true yes, false no - */ -bool cgexec_is_same_group(const char* oldwd, const char* newwd) -{ - int len = strlen(newwd); - - if (':' == oldwd[len]) - return strncmp(oldwd, newwd, len) == 0; - - return false; -} - -/* - * function name: cgexec_create_class_cgroup - * description : when non-root user wants to create Class Cgroup or - * Workload Cgroup, it calls this function to do the things. - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_create_class_cgroup(void) -{ - int i, cls = 0, find = 0; - int percent = 0; - char* toppath = NULL; - - /* check if the class exists */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) { - if (cls == 0) - cls = i; - continue; - } - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { - find = 1; - cls = i; - break; - } - } - - /* back up the config file */ - if (-1 == cgconf_backup_config_file()) { - return -1; - } - - /* create cgroup if it doesn't exist */ - if (find == 0) { - if (cls == 0) { - fprintf(stderr, "ERROR: failed to create %s cgroup for there is no class item!\n", cgutil_opt.clsname); - return -1; - } else /* create cgroup */ - { - /* check the remain percentage */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used) - percent += cgutil_vaddr[i]->ginfo.cls.percent; - } - - if (cgutil_opt.clspct) { - if (cgutil_opt.clspct > (GROUP_ALL_PERCENT - percent)) { - fprintf(stderr, - "ERROR: there is no more resource for new cgroup %s.\n" - "the remain percentage is %d.\n", - cgutil_opt.clsname, - GROUP_ALL_PERCENT - percent); - return -1; - } - } else { - if (DEFAULT_CLASS_PERCENT > (GROUP_ALL_PERCENT - percent)) - cgutil_opt.clspct = GROUP_ALL_PERCENT - percent; - else - cgutil_opt.clspct = DEFAULT_CLASS_PERCENT; - - if (cgutil_opt.clspct == 0) { - fprintf(stderr, - "ERROR: there is no more resource for new cgroup %s.\n" - "the remain percentage is %d.\n", - cgutil_opt.clsname, - GROUP_ALL_PERCENT - percent); - return -1; - } - } - - /* set the cgutil_vaddr item */ - cgconf_set_class_group(cls); - - if (-1 == cgexec_create_new_cgroup(cgutil_vaddr[cls])) { - cgconf_reset_class_group(cls); - return -1; - } - - /* set the top wd item */ - for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - break; - } - - cgconf_set_top_workload_group(i, cls); - - /* create the workload cgroup */ - if (-1 == cgexec_create_workload_cgroup(cgutil_vaddr[i])) { - cgconf_reset_workload_group(i); - return -1; - } - } - } else { - if (!cgutil_opt.wdname[0]) { - fprintf(stderr, "ERROR: cannot create existed class %s.\n", cgutil_opt.clsname); - return -1; - } - - if (cgutil_opt.clssetpct == 1 || cgutil_opt.clspct) { - fprintf(stderr, - "ERROR: cannot specify existed class %s and \"-s\" together when create control group\n", - cgutil_opt.clsname); - return -1; - } - } - - /* find the group item if it is specified */ - if (cgutil_opt.wdname[0]) { - if (cgutil_vaddr[cls]->ginfo.cls.maxlevel == MAX_WD_LEVEL) { - fprintf(stderr, - "ERROR: failed to create %s cgroup " - "for %s cgroup has reach the maximum level!\n", - cgutil_opt.wdname, - cgutil_opt.clsname); - return -1; - } - - for (i = WDCG_START_ID; i <= WDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - break; - - if (cgutil_vaddr[i]->ginfo.wd.cgid == cls && - cgexec_is_same_group(cgutil_vaddr[i]->grpname, cgutil_opt.wdname)) { - fprintf(stderr, - "ERROR: failed to create %s cgroup " - "for %s has been existed for class %s \n", - cgutil_opt.wdname, - cgutil_vaddr[i]->grpname, - cgutil_vaddr[cls]->grpname); - return -1; - } - } - - /* should make sure there is resource for timeshare cgroup */ - if (cgutil_opt.grppct) { - if (cgutil_vaddr[cls]->ginfo.cls.rempct <= cgutil_opt.grppct) { - fprintf(stderr, - "ERROR: there is no more resource for new cgroup %s.\n" - "the remain percentage is %d, available percentage is %d.\n", - cgutil_opt.wdname, - cgutil_vaddr[cls]->ginfo.cls.rempct, - cgutil_vaddr[cls]->ginfo.cls.rempct - 1); - return -1; - } - } else { - if (DEFAULT_WORKLOAD_PERCENT >= cgutil_vaddr[cls]->ginfo.cls.rempct) - cgutil_opt.grppct = cgutil_vaddr[cls]->ginfo.cls.rempct - 1; - else - cgutil_opt.grppct = DEFAULT_WORKLOAD_PERCENT; - } - - /* if timeshare has been created, drop them */ - if (find) { - /* get the top timeshare path */ - toppath = gscgroup_get_topts_path(cgutil_vaddr[cls]->gid, cgutil_vaddr, current_nodegroup); - if (NULL == toppath) - return -1; - - if (-1 == cgexec_delete_cgroups(toppath)) { - free(toppath); - toppath = NULL; - return -1; - } - - free(toppath); - toppath = NULL; - } - - cgconf_set_workload_group(i, cls); - - /* create the workload cgroup */ - if (-1 == cgexec_create_workload_cgroup(cgutil_vaddr[i])) { - cgconf_reset_workload_group(i); - return -1; - } - - /* create timeshare cgroup */ - if (-1 == cgexec_create_timeshare_cgroup(cgutil_vaddr[cls])) - return -1; - } else { - if (find == 0 && -1 == cgexec_create_timeshare_cgroup(cgutil_vaddr[cls])) - return -1; - } - - return 0; -} - -/* - * function name: cgexec_delete_remain_cgroup - * description : When one workload Cgroup is deleted, it needs to call - * this function to delete the last remain Cgroup and its - * Child Cgroups(timeshare Cgroup) - * arguments : - * grp: the configuration of workload Cgroup which is the same level - * as the remain Cgroup - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_delete_remain_cgroup(gscgroup_grp_t* grp) -{ - char* relpath = NULL; - char rempath[16]; - errno_t sret; - - relpath = gscgroup_get_parent_wdcg_path(grp->gid, cgutil_vaddr, current_nodegroup); - if (NULL == relpath) - return -1; - - /* add the remain path dir */ - sret = snprintf_s( - rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d", GSCGROUP_REMAIN_WORKLOAD, grp->ginfo.wd.wdlevel); - securec_check_intval(sret, free(relpath), -1); - sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); - securec_check_errno(sret, free(relpath), -1); - - (void)cgexec_delete_cgroups(relpath); - - free(relpath); - relpath = NULL; - - return 0; -} - -/* - * function name: cgexec_copy_next_level_cgroup - * description : copy the specified workload Cgroup the upper level. - * arguments : - * relpath: the parent path of deleted cgroup - * grp: the configuration of workload group - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_copy_next_level_cgroup(const char* relpath, gscgroup_grp_t* grp) -{ - int ret; - char grpname[GPNAME_LEN]; - char* wdpath = NULL; - char* p = NULL; - struct cgroup *oldcg = NULL, *newcg = NULL; - struct cgroup_controller* cgc[MOUNT_SUBSYS_KINDS]; - errno_t sret; - - /* get the current path of specified Cgroup */ - wdpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup); - if (NULL == wdpath) { - fprintf(stderr, "ERROR: failed to allocate memory for path!\n"); - return -1; - } - - /* allocate new cgroup structure */ - oldcg = cgroup_new_cgroup(wdpath); - if (oldcg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", wdpath); - free(wdpath); - wdpath = NULL; - return -1; - } - - /* get all information regarding the cgroup from kernel */ - ret = cgroup_get_cgroup(oldcg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", wdpath, cgroup_strerror(ret), ret); - free(wdpath); - wdpath = NULL; - cgroup_free(&oldcg); - return -1; - } - - sret = memset_s(wdpath, GPNAME_PATH_LEN, 0, GPNAME_PATH_LEN); - securec_check_errno(sret, free(wdpath); cgroup_free(&oldcg), -1); - - /* get the grpname without level */ - sret = strcpy_s(grpname, GPNAME_LEN, grp->grpname); - securec_check_errno(sret, free(wdpath); cgroup_free(&oldcg);, -1); - - if ((p = strchr(grpname, ':')) != NULL) - *p = '\0'; - - /* get the new path of the workload cgroup */ - sret = snprintf_s( - wdpath, GPNAME_PATH_LEN, GPNAME_PATH_LEN - 1, "%s%s:%d", relpath, grpname, grp->ginfo.wd.wdlevel - 1); - securec_check_intval(sret, free(wdpath); cgroup_free(&oldcg), -1); - - /* allocate new cgroup structure */ - newcg = cgroup_new_cgroup(wdpath); - if (newcg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", wdpath); - free(wdpath); - wdpath = NULL; - cgroup_free(&oldcg); - return -1; - } - - /* set the uid and gid */ - ret = cgroup_set_uid_gid(newcg, - cgutil_passwd_user->pw_uid, - cgutil_passwd_user->pw_gid, - cgutil_passwd_user->pw_uid, - cgutil_passwd_user->pw_gid); - if (ret) { - fprintf(stderr, "ERROR: failed to set uid and gid for %s!\n", cgroup_strerror(ret)); - free(wdpath); - wdpath = NULL; - cgroup_free(&oldcg); - cgroup_free(&newcg); - return -1; - } - - /* add the controller */ - for (int i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - if (i == MOUNT_BLKIO_ID || i == MOUNT_MEMORY_ID) - continue; - - cgc[i] = cgroup_add_controller(newcg, cgutil_subsys_table[i]); - - if (cgc[i] == NULL) { - fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", cgutil_subsys_table[i], grp->grpname); - - cgroup_free(&oldcg); - - goto error; - } - } - - ret = cgroup_create_cgroup(newcg, 0); - if (ret) { - fprintf(stderr, "ERROR: can't create cgroup for %s\n", cgroup_strerror(ret)); - cgroup_free(&oldcg); - goto error; - } - - /* copy the group from old cg */ - ret = cgroup_copy_cgroup(newcg, oldcg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to copy cgroup information for %s(%d)\n", cgroup_strerror(ret), ret); - cgroup_free(&oldcg); - goto error; - } - - cgroup_free(&oldcg); - - ret = cgroup_modify_cgroup(newcg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to modify cgroup information for %s(%d)\n", cgroup_strerror(ret), ret); - goto error; - } - - free(wdpath); - wdpath = NULL; - cgroup_free_controllers(newcg); - cgroup_free(&newcg); - - /* delete the old one */ - if (-1 == cgexec_delete_default_cgroup(grp)) - return -1; - - /* update the workload group */ - grp->ginfo.wd.wdlevel -= 1; - - sret = snprintf_s( - grp->grpname, sizeof(grp->grpname), sizeof(grp->grpname) - 1, "%s:%d", grpname, grp->ginfo.wd.wdlevel); - securec_check_intval(sret, , -1); - - return 0; - -error: - free(wdpath); - wdpath = NULL; - cgroup_free_controllers(newcg); - cgroup_free(&newcg); - return -1; -} - -/* - * function name: cgexec_delete_workload_cgroup - * description : delete the specified workload Cgroup - * arguments : - * grp: the configuration of workload group - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_delete_workload_cgroup(gscgroup_grp_t* grp) -{ - int wgid = grp->gid; - int wglevel = grp->ginfo.wd.wdlevel; - int cgid = grp->ginfo.wd.cgid; - gscgroup_grp_t* cls_grp = cgutil_vaddr[cgid]; - int i, j, ret, rempct = GROUP_ALL_PERCENT; - int cpushares, ioweight; - char* relpath = NULL; - char rempath[16]; - errno_t sret; - - /* it is the last level workload group */ - if (wglevel == cls_grp->ginfo.cls.maxlevel) { - /* delete remain cgroup */ - if (-1 == cgexec_delete_remain_cgroup(grp)) - return -1; - - /* delete workload cgroup */ - if (-1 == cgexec_delete_default_cgroup(grp)) - return -1; - - /* reset remain percent */ - cls_grp->ginfo.cls.rempct += grp->ginfo.wd.percent; - - cgconf_reset_workload_group(grp->gid); - } else { - /* delete the first one */ - if (-1 == cgexec_delete_default_cgroup(grp)) - return -1; - - if (NULL == (relpath = gscgroup_get_parent_wdcg_path(grp->gid, cgutil_vaddr, current_nodegroup))) - return -1; - - /* count from 2 is for discarding the TopWD group */ - for (i = 2; i < wglevel; i++) { - /* calculate the remain percentage */ - for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { - if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cgid && - cgutil_vaddr[j]->ginfo.wd.wdlevel == i) - break; - } - - rempct -= cgutil_vaddr[j]->ginfo.wd.percent; - } - - cls_grp->ginfo.cls.rempct += grp->ginfo.wd.percent; - - /* reset, can't use grp */ - cgconf_reset_workload_group(wgid); - - for (i = wglevel; i < cls_grp->ginfo.cls.maxlevel; i++) { - /* get the next level workload */ - for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { - if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cgid && - cgutil_vaddr[j]->ginfo.wd.wdlevel == (i + 1)) - break; - } - - /* copy the next workload into this level */ - if (-1 == cgexec_copy_next_level_cgroup(relpath, cgutil_vaddr[j])) { - free(relpath); - relpath = NULL; - return -1; - } - - /* add the remain path dir */ - sret = snprintf_s(rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d/", GSCGROUP_REMAIN_WORKLOAD, i); - securec_check_intval(sret, free(relpath), -1); - sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); - securec_check_errno(sret, free(relpath), -1); - - /* update the remain cgroup */ - rempct -= cgutil_vaddr[j]->ginfo.wd.percent; - - cpushares = MAX_CLASS_CPUSHARES * rempct / GROUP_ALL_PERCENT; - ioweight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, rempct); - - ret = cgexec_update_remain_value(relpath, cpushares, ioweight); - if (-1 == ret) { - free(relpath); - relpath = NULL; - return -1; - } - } - - /* add the remain path dir */ - sret = snprintf_s(rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d/", GSCGROUP_REMAIN_WORKLOAD, i); - securec_check_intval(sret, free(relpath), -1); - sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); - securec_check_errno(sret, free(relpath), -1); - - (void)cgexec_delete_cgroups(relpath); - free(relpath); - relpath = NULL; - } - - cls_grp->ginfo.cls.maxlevel -= 1; - - if (-1 == cgexec_create_timeshare_cgroup(cgutil_vaddr[cgid])) - return -1; - - return 0; -} - -/* - * function name: cgexec_delete_class_cgroup - * description : delete the class Cgroup and workload Cgroup based on options - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_delete_class_cgroup(void) -{ - int i, cls = 0, wd = 0; - - /* check if the class exists */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { - cls = i; - break; - } - } - - /* backup the config file for recovery */ - if (-1 == cgconf_backup_config_file()) { - return -1; - } - - if (cls) { - if (cgutil_opt.wdname[0]) { - wd = cgexec_search_workload_group(cls); - - if (wd) { - (void)cgexec_delete_workload_cgroup(cgutil_vaddr[wd]); - } else { - fprintf(stderr, "ERROR: the specified workload %s doesn't exist!\n", cgutil_opt.wdname); - return -1; - } - } else { - if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls])) - return -1; - - cgconf_reset_class_group(cls); - } - } else { - fprintf(stderr, "ERROR: the specified class %s doesn't exist!\n", cgutil_opt.clsname); - return -1; - } - - return 0; -} - -/* - * @Description: update cgroup cpuset value. - * @IN relpath: relpath of the cgroup - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_cgroup_cpuset_value(char* relpath, char* cpuset) -{ - int ret; - struct cgroup_controller* cgc = NULL; - struct cgroup* cg = NULL; - - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); - return -1; - } - - /* get all information regarding the cgroup from kernel */ - ret = cgroup_get_cgroup(cg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); - cgroup_free(&cg); - return -1; - } - - /* get the CPUSET controller */ - cgc = cgroup_get_controller(cg, MOUNT_CPUSET_NAME); - if (NULL == cgc) { - cgroup_free(&cg); - return -1; - } - - /* get cpuset value with controller */ - if (0 != (ret = cgroup_set_value_string(cgc, CPUSET_CPUS, cpuset))) { - fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, cpuset, cgroup_strerror(ret)); - goto error; - } - - /* modify the value into kernel */ - if (0 != (ret = cgroup_modify_cgroup(cg))) { - fprintf(stderr, - "ERROR: failed to modify cgroup for %s " - "when updating group!\n", - cgroup_strerror(ret)); - goto error; - } - - cgroup_free_controllers(cg); - cgroup_free(&cg); - - return 0; - -error: - cgroup_free_controllers(cg); - cgroup_free(&cg); - return -1; -} - -/* - * @Description: update timeshare group cpuset value. - * @IN grp: group info - * @IN cpuset: cpuset value - * @IN update: 0: update remain cgroup cpuset value and then update timeshare - * not 0: update timeshrare cpuset value and then update remain group - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_timeshare_cpuset(gscgroup_grp_t* grp, char* cpuset, unsigned char update) -{ - char* toppath = NULL; - char* relpath = NULL; - int i; - - /* get the top timeshare path */ - toppath = gscgroup_get_topts_path(grp->gid, cgutil_vaddr, current_nodegroup); - if (NULL == toppath) - return -1; - - /* If update flag is 0, we must update the toppath cgroup with cpuset value first */ - if (update == 0 && cgexec_update_cgroup_cpuset_value(toppath, cpuset) == -1) { - fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); - free(toppath); - toppath = NULL; - return -1; - } - - /* allocate memory for path of timeshare cgroup */ - if (NULL == (relpath = (char*)malloc(GPNAME_PATH_LEN))) { - fprintf(stderr, "ERROR: failed to allocate memory for path!\n"); - free(toppath); - toppath = NULL; - return -1; - } - - /* update all timeshare group cpuset value */ - for (i = TSCG_START_ID; i <= TSCG_END_ID; ++i) { - int rc = snprintf_s(relpath, GPNAME_PATH_LEN, GPNAME_PATH_LEN - 1, "%s/%s", toppath, cgutil_vaddr[i]->grpname); - securec_check_intval(rc, free(toppath); free(relpath), -1); - - if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { - fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); - goto error; - } - } - - /* if update is not 0, we can update timeshare group first, and then toppath group */ - if (update && cgexec_update_cgroup_cpuset_value(toppath, cpuset) == -1) { - fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); - goto error; - } - - free(toppath); - toppath = NULL; - free(relpath); - relpath = NULL; - - return 0; - -error: - free(toppath); - toppath = NULL; - free(relpath); - relpath = NULL; - return -1; -} - -/* - * @Description: update group cpuset value. - * @IN grp: group info - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_cgroup_cpuset(gscgroup_grp_t* grp, char* cpuset) -{ - char* relpath = NULL; - - if (strcmp(grp->cpuset, cpuset) == 0) - return 0; - - /* get the relative path */ - if (NULL == (relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup))) - return -1; - - /* update cgroup cpuset value with relative path */ - if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { - fprintf(stderr, "ERROR: failed to update %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); - free(relpath); - relpath = NULL; - return -1; - } - - /* save new value as class cpuset value */ - errno_t sret = snprintf_s(grp->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); - securec_check_intval(sret, free(relpath), -1); - - free(relpath); - relpath = NULL; - - return 0; -} - -/* - * @Description: update 'topwd' group cpuset value. - * @IN cls: class group id - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_topwd_cgroup_cpuset(int cls, char* cpuset) -{ - char* relpath = NULL; - int i; - char topwd[GPNAME_LEN]; - - /* get 'topwd' cgroup full name*/ - errno_t rc = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); - securec_check_intval(rc, , -1); - - /* set the top wd item */ - for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { - if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->ginfo.wd.cgid == cls && - strcmp(cgutil_vaddr[i]->grpname, topwd) == 0) - break; - } - - /* find 'topwd' group failed */ - if (i > WDCG_END_ID) { - fprintf(stderr, "ERROR: Cannot find topwd for class: %s\n", cgutil_vaddr[i]->grpname); - return -1; - } - - /* get the relative path */ - if (NULL == (relpath = gscgroup_get_relative_path(cgutil_vaddr[i]->gid, cgutil_vaddr, current_nodegroup))) - return -1; - - /* update group cpuset value with relative path */ - if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { - fprintf(stderr, - "ERROR: failed to add %s controller in %s:%s!\n", - MOUNT_CPUSET_NAME, - cgutil_vaddr[cls]->grpname, - topwd); - free(relpath); - relpath = NULL; - return -1; - } - - /* save new value as class cpuset value */ - errno_t sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); - securec_check_intval(sret, free(relpath);, -1); - - free(relpath); - relpath = NULL; - - return 0; -} - -/* - * @Description: check all workload group cpuset value for the class. - * @IN cls: class group id - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_check_workload_cgroup_cpuset(int cls, const char* cpuset) -{ - int i; - char topwd[GPNAME_LEN]; - - /* get 'topwd' full name */ - errno_t rc = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); - securec_check_intval(rc, , -1); - - /* set all worload cpuset */ - for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { - if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->ginfo.wd.cgid == cls && - strcmp(cgutil_vaddr[i]->grpname, topwd) != 0) { - if (cgexec_check_cpuset_value(cpuset, cgutil_vaddr[i]->cpuset) < 0) { - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[i], cgutil_vaddr[cls]->cpuset) == -1) - return -1; - } - } - } - - return 0; -} - -/* - * @Description: update all workload group cpuset value. - * @IN grp: group info - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_all_workload_cgroup_cpuset(int cls, char* cpuset) -{ - int i; - char topwd[GPNAME_LEN]; - - /* get 'topwd' full name */ - errno_t rc = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); - securec_check_intval(rc, , -1); - - /* set all worload cpuset */ - for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { - if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->ginfo.wd.cgid == cls && - strcmp(cgutil_vaddr[i]->grpname, topwd) != 0) { - /* if the cpuset of upper levels groups alter larger, workload groups will be altered larger, too*/ - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[i], cpuset) == -1) - return -1; - } - } - - return 0; -} - -/* - * @Description: update remain group cpuset value. - * @IN cls: class group id - * @IN level: remain group level id - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_remain_cgroup_cpuset_value(int cls, int level, char* cpuset) -{ - char* relpath = NULL; - char rempath[16]; - int j; - errno_t sret; - - for (j = WDCG_START_ID; j <= WDCG_END_ID; ++j) { - if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cls && - cgutil_vaddr[j]->ginfo.wd.wdlevel == level) - break; - } - - /* get the parent path of the workload group */ - relpath = gscgroup_get_parent_wdcg_path(j, cgutil_vaddr, current_nodegroup); - if (NULL == relpath) - return -1; - - /* get the remain group path */ - sret = snprintf_s(rempath, - sizeof(rempath), - sizeof(rempath) - 1, - "%s:%d/", - GSCGROUP_REMAIN_WORKLOAD, - cgutil_vaddr[j]->ginfo.wd.wdlevel); - securec_check_intval(sret, free(relpath), -1); - - sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); - securec_check_errno(sret, free(relpath), -1); - - /*update cgroup cpuset with relative path */ - if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { - fprintf(stderr, - "ERROR: failed to add %s controller in %s:%d!\n", - MOUNT_CPUSET_NAME, - GSCGROUP_REMAIN_WORKLOAD, - cgutil_vaddr[j]->ginfo.wd.wdlevel); - free(relpath); - relpath = NULL; - return -1; - } - - free(relpath); - relpath = NULL; - - return 0; -} - -/* - * @Description: update remain group cpuset. - * @IN cls: class group id - * @IN cpuset: cpuset value - * @IN update: update flag - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_remain_cgroup_cpuset(int cls, char* cpuset, unsigned char update) -{ - int i; - - if (update) { - /* update the 'remain' group cpuset value from high level to low level */ - for (i = cgutil_vaddr[cls]->ginfo.cls.maxlevel; i >= 1; --i) { - if (cgexec_update_remain_cgroup_cpuset_value(cls, i, cpuset) == -1) { - fprintf(stderr, - "ERROR: failed to add %s controller in %s:%d, update: %d, cpuset: %s!\n", - MOUNT_CPUSET_NAME, - GSCGROUP_REMAIN_WORKLOAD, - i, - update, - cpuset); - return -1; - } - } - } else { - /* update the 'remain' group cpuset value from low level to high level */ - for (i = 1; i <= cgutil_vaddr[cls]->ginfo.cls.maxlevel; ++i) { - if (cgexec_update_remain_cgroup_cpuset_value(cls, i, cpuset) == -1) { - fprintf(stderr, - "ERROR: failed to add %s controller in %s:%d, update: %d, cpuset: %s!\n", - MOUNT_CPUSET_NAME, - GSCGROUP_REMAIN_WORKLOAD, - i, - update, - cpuset); - return -1; - } - } - } - - return 0; -} - -/* - * @Description: update 'class' group cpuset. - * @IN cls: class group id - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_class_cpuset(int cls, char* cpuset) -{ - char largeset[CPUSET_LEN]; - - (void)cgexec_check_workload_cgroup_cpuset(cls, cpuset); - - /* get large range with old cpuset and new cpuset */ - cgexec_get_large_cupset(cgutil_vaddr[cls]->cpuset, cpuset, largeset); - - /* - * If we will set a group new cpuset value, we must make - * sure the upper group has large range, we have to update - * the group with large set first, - * order: class -> remain -> timeshare - * after that, we can update the group cpuset value as we wish. - */ - if (strcmp(cgutil_vaddr[cls]->cpuset, largeset) != 0 && - (cgexec_update_cgroup_cpuset(cgutil_vaddr[cls], largeset) == -1 || - cgexec_update_remain_cgroup_cpuset(cls, largeset, 0) == -1 || - cgexec_update_timeshare_cpuset(cgutil_vaddr[cls], largeset, 0) == -1 || - cgexec_update_topwd_cgroup_cpuset(cls, cpuset) == -1)) { - fprintf(stderr, "ERROR: failed to update cpuset for group in %s!\n", cgutil_vaddr[cls]->grpname); - return -1; - } - - /* - * We set all workload group cpuset value with new value, it's - * safe to update their value because the upper group has large - * set value already, now we can update these upper group, - * order: timeshare -> remain -> class - */ - if (cgexec_update_all_workload_cgroup_cpuset(cls, cpuset) == -1 || - cgexec_update_timeshare_cpuset(cgutil_vaddr[cls], cpuset, 1) == -1 || - cgexec_update_remain_cgroup_cpuset(cls, cpuset, 1) == -1 || - cgexec_update_topwd_cgroup_cpuset(cls, cpuset) == -1 || - cgexec_update_cgroup_cpuset(cgutil_vaddr[cls], cpuset) == -1) { - fprintf(stderr, "ERROR: failed to update cpuset for timeshare group in %s!\n", cgutil_vaddr[cls]->grpname); - return -1; - } - - return 0; -} - -/* - * @Description: update all class group cpuset value. - * @IN grp: group info - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_all_class_cgroup_cpuset(char* cpuset) -{ - /* update all class group cpuset from default value to new cpuset */ - for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; ++i) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (cgexec_update_class_cpuset(i, cpuset) == -1) - return -1; - } - - return 0; -} - -/* - * @Description: update all backend group cpuset value. - * @IN grp: group info - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_all_backend_cgroup_cpuset(char* cpuset) -{ - /* update all backend group cpuset from default value to new cpuset */ - for (int i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; ++i) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[i], cpuset) == -1) - return -1; - } - - return 0; -} - -/* - * @Description: update top group cpuset top group, include: GAUSSDB, BACKEND, CLASS. - * @IN top: top group id - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_update_top_group_cpuset(int top, char* cpuset) -{ - if (top == TOPCG_GAUSSDB) { - char largeset[CPUSET_LEN]; - - DIR* dir = NULL; - struct dirent* de = NULL; - - char path[MAXPGPATH] = {0}; - char subpath[MAXPGPATH] = {0}; - struct stat statbuf; - errno_t rc; - int ret = -1; - bool ummap_flag = false; - - rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); - securec_check_errno(rc, , -1); - /* Update the default configuration file */ - cgexec_get_large_cupset(cgutil_vaddr[TOPCG_GAUSSDB]->cpuset, cpuset, largeset); - - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_GAUSSDB], largeset) == -1 || - cgexec_update_top_group_cpuset(TOPCG_BACKEND, cpuset) == -1 || - cgexec_update_top_group_cpuset(TOPCG_CLASS, cpuset) == -1) { - fprintf(stdout, "ERROR: update all cgroup cpuset failed.\n"); - return -1; - } - - rc = snprintf_s(path, - sizeof(path), - sizeof(path) - 1, - "%s/%s:%s", - cgutil_opt.mpoints[0], - GSCGROUP_TOP_DATABASE, - cgutil_passwd_user->pw_name); - securec_check_intval(rc, , -1); - - if (NULL == (dir = opendir(path))) - return -1; - - while (NULL != (de = readdir(dir))) { - if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) - continue; - - rc = snprintf_s(subpath, sizeof(subpath), sizeof(subpath) - 1, "%s/%s", path, de->d_name); - securec_check_intval(rc, (void)closedir(dir);, -1); - - /* check if it is directory */ - ret = stat(subpath, &statbuf); - if (0 != ret || !S_ISDIR(statbuf.st_mode)) - continue; - - if (NULL != strstr(de->d_name, GSCGROUP_TOP_BACKEND)) - continue; - - if (NULL != strstr(de->d_name, GSCGROUP_TOP_CLASS)) - continue; - - if (cgutil_vaddr[0] != NULL) { - (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - ummap_flag = true; - } - - rc = snprintf_s( - cgutil_opt.nodegroup, sizeof(cgutil_opt.nodegroup), sizeof(cgutil_opt.nodegroup) - 1, "%s", de->d_name); - securec_check_intval(rc, (void)closedir(dir);, -1); - - current_nodegroup = cgutil_opt.nodegroup; - - /* get the configuration infor of logical cluster */ - if (-1 == cgconf_parse_nodegroup_config_file()) { - (void)closedir(dir); - return -1; - } - - /* update the cpuset of logical cluster */ - if (cgexec_update_top_group_cpuset(TOPCG_CLASS, cpuset) == -1) { - fprintf( - stdout, "ERROR: update all cgroup cpuset of %s logical cluster failed.\n", cgutil_opt.nodegroup); - (void)closedir(dir); - return -1; - } - - if (cgexec_reset_cpuset_cgroups(TOPCG_CLASS, 0) == -1) { - fprintf(stdout, "ERROR: failed to reset cpuset of %s logical cluster.\n", cgutil_opt.nodegroup); - (void)closedir(dir); - return -1; - } - } - - (void)closedir(dir); - - /* reset nodegroup */ - if (ummap_flag == true) { - *cgutil_opt.nodegroup = '\0'; - current_nodegroup = NULL; - if (-1 == cgconf_parse_config_file()) { - fprintf(stdout, "ERROR: failed to parse the default configuration file.\n"); - return -1; - } - } - return cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_GAUSSDB], cpuset); - } - - if (top == TOPCG_BACKEND) { - char largeset[CPUSET_LEN]; - - /* get large range with old cpuset and new cpuset */ - cgexec_get_large_cupset(cgutil_vaddr[TOPCG_BACKEND]->cpuset, cpuset, largeset); - - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_BACKEND], largeset) == -1 || - cgexec_update_all_backend_cgroup_cpuset(cpuset) == -1) { - fprintf(stdout, "ERROR: update Backend cpuset failed.\n"); - return -1; - } - - /* update 'DEFAULT_BACKEND' and 'VACUUM' group cpuset, and then update 'BACKEND' to new cpuset value */ - return cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_BACKEND], cpuset); - } - - if (top == TOPCG_CLASS) { - char largeset[CPUSET_LEN]; - - /* get large range with old cpuset and new cpuset */ - cgexec_get_large_cupset(cgutil_vaddr[TOPCG_CLASS]->cpuset, cpuset, largeset); - - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_CLASS], largeset) == -1 || - cgexec_update_all_class_cgroup_cpuset(cpuset) == -1) { - fprintf(stdout, "ERROR: update Class cpuset failed.\n"); - return -1; - } - - /* update 'CLASS' group to new cpuset value */ - return cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_CLASS], cpuset); - } - - return 0; -} - -/* - * function name: cgexec_update_dynamic_class_cgroup - * description : when the dynamic value of class cgroup or - * workload cgroup is update, it updates the class configuration - * and workload configuration corresponding. - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_dynamic_class_cgroup(void) -{ - int i, cls = 0, wd = 0; - int percent = 0; - - /* check if the class exists */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { - cls = i; - break; - } - } - - if (cls) { - if (cgutil_opt.clspct && (cgutil_opt.clspct > cgutil_vaddr[cls]->ginfo.cls.percent)) { - /* check the remain percentage */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used && (i != cls)) - percent += cgutil_vaddr[i]->ginfo.cls.percent; - } - - if ((GROUP_ALL_PERCENT - percent) < cgutil_opt.clspct) { - fprintf(stderr, - "ERROR: there is no more resource for updated cgroup %s.\n" - "the remain percentage is %d.\n", - cgutil_opt.clsname, - GROUP_ALL_PERCENT - percent); - return -1; - } - } - - if (cgutil_opt.clspct && cgutil_opt.clspct != cgutil_vaddr[cls]->ginfo.cls.percent) { - /* set the cgutil_vaddr item */ - cgconf_update_class_group(cgutil_vaddr[cls]); - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[cls])) - return -1; - } - - if (cgutil_opt.wdname[0]) { - wd = cgexec_search_workload_group(cls); - - if (wd && cgutil_opt.grppct) { - if (cgutil_opt.grppct >= (cgutil_vaddr[cls]->ginfo.cls.rempct + cgutil_vaddr[wd]->ginfo.wd.percent)) { - fprintf(stderr, - "ERROR: there is no more resource for updated cgroup %s.\n" - "the remain percentage is %d.\n", - cgutil_opt.wdname, - cgutil_vaddr[cls]->ginfo.cls.rempct + cgutil_vaddr[wd]->ginfo.wd.percent); - return -1; - } - - if (cgutil_opt.grppct != cgutil_vaddr[wd]->ginfo.wd.percent) { - cgconf_update_workload_group(cgutil_vaddr[wd]); - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[wd])) - return -1; - - if (-1 == cgexec_update_remain_cgroup(cgutil_vaddr[wd], cls)) - return -1; - } - } else if (wd == 0) { - fprintf(stderr, "ERROR: the specified workload group %s doesn't exist!\n", cgutil_opt.wdname); - return -1; - } - } - } else { - fprintf(stderr, "ERROR: the specified class group %s doesn't exist!\n", cgutil_opt.clsname); - return -1; - } - - return 0; -} - -/* - * @Description: check dynamic backend percent. - * @IN bkd: backend group id - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int cgexec_check_dynamic_backend_percent(int bkd) -{ - int i = 0; - int percent = 0; - - if (cgutil_opt.bkdpct > 0 && cgutil_opt.bkdpct > cgutil_vaddr[bkd]->ginfo.cls.percent) { - /* check the remain percentage */ - for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used && (i != bkd)) - percent += cgutil_vaddr[i]->ginfo.cls.percent; - } - - if ((GROUP_ALL_PERCENT - percent) < cgutil_opt.bkdpct) { - fprintf(stderr, - "ERROR: there is no more resource for updated cgroup %s.\n" - "the remain percentage is %d.\n", - cgutil_opt.bkdname, - GROUP_ALL_PERCENT - percent); - return -1; - } - } - - if (cgutil_opt.bkdpct > 0 && cgutil_opt.bkdpct != cgutil_vaddr[bkd]->ginfo.cls.percent) { - /* set the cgutil_vaddr item */ - cgconf_update_backend_group(cgutil_vaddr[bkd]); - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[bkd])) - return -1; - } - - return 0; -} - -/* - * function name: cgexec_update_dynamic_backend_cgroup - * description : update the dynamic value of backend cgroup - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_dynamic_backend_cgroup(void) -{ - int i, bkd = 0; - - /* check if the class exists */ - for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.bkdname)) { - bkd = i; - break; - } - } - - if (bkd) { - if (cgexec_check_dynamic_backend_percent(bkd) == -1) - return -1; - } else { - fprintf(stderr, "ERROR: the specified backend group %s doesn't exist!\n", cgutil_opt.bkdname); - return -1; - } - - return 0; -} - -/* - * function name: cgexec_update_top_group_percent - * description : update the dynamic value of Top Cgroup; it include - * Root Cgroup, Guassdb:user Cgroup, Class Cgroup - * and Backend Cgroup. - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_top_group_percent(void) -{ - if (0 == strcmp(cgutil_opt.topname, GSCGROUP_ROOT)) { - if (geteuid() != 0) { - fprintf(stderr, "ERROR: non-root user can't modify the Root cgroup!\n"); - return -1; - } - - if (cgutil_opt.toppct < 10) { - cgutil_opt.toppct = 10; - } - - if (cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent == cgutil_opt.toppct) - return 0; - - cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = cgutil_opt.toppct; - - cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = MAX_IO_WEIGHT * cgutil_opt.toppct / GROUP_ALL_PERCENT; - - if ((cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) && - (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_ROOT]))) - return -1; - } else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE) || - 0 == strcmp(cgutil_opt.topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname)) { - if (cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent == cgutil_opt.toppct) - return 0; - - cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent = cgutil_opt.toppct; - cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.shares = - DEFAULT_CPU_SHARES * cgutil_opt.toppct / (GROUP_ALL_PERCENT - cgutil_opt.toppct); - cgutil_vaddr[TOPCG_GAUSSDB]->percent = - cgutil_vaddr[TOPCG_ROOT]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_GAUSSDB])) - return -1; - - cgconf_update_top_percent(); - } else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_BACKEND)) { - if (cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent == cgutil_opt.toppct) - return 0; - - cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = cgutil_opt.toppct; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_opt.toppct / 10; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_opt.toppct); - cgutil_vaddr[TOPCG_BACKEND]->percent = - cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_BACKEND])) - return -1; - - cgconf_update_backend_percent(); - - cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = GROUP_ALL_PERCENT - cgutil_opt.toppct; - cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / 10; - cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, GROUP_ALL_PERCENT - cgutil_opt.toppct); - cgutil_vaddr[TOPCG_CLASS]->percent = - cgutil_vaddr[TOPCG_GAUSSDB]->percent * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / GROUP_ALL_PERCENT; - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_CLASS])) - return -1; - - cgconf_update_class_percent(); - } else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_CLASS)) { - if (cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent == cgutil_opt.toppct) - return 0; - - cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = cgutil_opt.toppct; - cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_opt.toppct / 10; - cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_opt.toppct); - cgutil_vaddr[TOPCG_CLASS]->percent = - cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_CLASS])) - return -1; - - cgconf_update_class_percent(); - - cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = GROUP_ALL_PERCENT - cgutil_opt.toppct; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / 10; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = - IO_WEIGHT_CALC(MAX_IO_WEIGHT, GROUP_ALL_PERCENT - cgutil_opt.toppct); - cgutil_vaddr[TOPCG_BACKEND]->percent = - cgutil_vaddr[TOPCG_GAUSSDB]->percent * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / GROUP_ALL_PERCENT; - - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_BACKEND])) - return -1; - - cgconf_update_backend_percent(); - } else { - fprintf(stderr, "ERROR: the specified top group %s doesn't exist!\n", cgutil_opt.topname); - return -1; - } - - return 0; -} -/* - * function name: cgexec_update_top_group_cpuset_userset - * description : update top level cpuset by user set "-f" - * @IN topname : top group name to be updated. - * @IN cpuset : user set cpuset to be updated. - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_update_top_group_cpuset_userset(const char* topname, char* cpuset) -{ - int topstart = 0; - int topend = 0; - int toplength = 0; - - int rcs = sscanf_s(cpuset, "%d-%d", &topstart, &topend); - if (rcs != 2) { - fprintf(stderr, - "%s:%d failed on calling " - "security function.\n", - __FILE__, - __LINE__); - return -1; - } - - toplength = topend - topstart + 1; - - /* we cannot changed the root group */ - if (strcmp(topname, GSCGROUP_ROOT) == 0) { - fprintf(stdout, "ERROR: cpuset of Root can not be changed.\n"); - return -1; - } - if (strcmp(topname, GSCGROUP_TOP_DATABASE) == 0 || strcmp(topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname) == 0) { - /* - * when updating cpuset for top classes, - * we need update all the belonging lower level groups by percentage. - */ - if (cgexec_update_top_group_cpuset(TOPCG_GAUSSDB, cpuset) == -1 || - cgexec_reset_cpuset_cgroups(TOPCG_GAUSSDB, 0) == -1) - return -1; - /* - * each time we use "-f" to set cpuset, - * we need reset quota to let this group not be influenced next time - * when we set cpuset percentage. - */ - cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.quota = 0; - } - - return 0; -} -/* - * function name: cgexec_update_dynamic_top_cgroup - * description : update the dynamic value of Top Cgroup; it include - * Root Cgroup, Guassdb:user Cgroup, Class Cgroup - * and Backend Cgroup. - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_dynamic_top_cgroup(void) -{ - if (cgutil_opt.toppct > 0 && cgexec_update_top_group_percent() == -1) { - return -1; - } - - if (*cgutil_opt.sets) - return cgexec_update_top_group_cpuset_userset(cgutil_opt.topname, cgutil_opt.sets); - - return 0; -} - -/* - * function name: cgexec_update_fixed_class_cgroup - * description : update the cpuset value of Class group and Workload group by user set "--fixed" - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_fixed_class_cgroup(void) -{ - int i, cls = 0, wd = 0; - char cpusets[CPUSET_LEN]; - int need_reset = 0; - - /* check if the class exists */ - for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { - cls = i; - break; - } - } - - if (cls) { - if (cgutil_opt.wdname[0]) { - wd = cgexec_search_workload_group(cls); - - if (wd) { - if (cgutil_opt.setspct) { - /* - * step 1 check whether the newly set percentage makes the whole percentage higher than 100%. - * result: - * setslength = -1:higher than 100%. - * setslength = 0: cpusets have been set well, go to step 3. - * setslength > 0: cpusets is empty yet and need reset, go to step 2. - */ - if ((need_reset = cgexec_check_cpuset_percent(cls, wd, cpusets)) == -1) - return -1; - - /*step 2: defragment the other workload groups. */ - if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(cls, wd) == -1)) - return -1; - - /* step 3: update the workload group. */ - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[wd], cpusets) == -1) - return -1; - - cgutil_vaddr[wd]->ainfo.quota = cgutil_opt.setspct; - - return 0; - } else if (cgutil_opt.setfixed) { - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[wd], cgutil_vaddr[cls]->cpuset) == -1) - return -1; - - cgutil_vaddr[wd]->ainfo.quota = 0; - - return 0; - } - } else { - fprintf(stderr, "ERROR: the specified workload group %s doesn't exist!\n", cgutil_opt.wdname); - return -1; - } - } - if (cgutil_opt.setspct) { - /* - * step 1: check whether the newly set percentage of class is legal or not. - * return value = -1: illegal - * return value = 0: legal and no need to do defragment. - * return value > 0: need defragment - */ - if ((need_reset = cgexec_check_cpuset_percent(TOPCG_CLASS, cls, cpusets)) == -1) - return -1; - - /* step 2: degragment the other class groups, reset their belonging workload groups by percentage. */ - if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(TOPCG_CLASS, cls) == -1)) - return -1; - - /*step 3: update the class group. */ - if (cgexec_update_class_cpuset(cls, cpusets) == -1) { - fprintf(stderr, "ERROR: update cpuset for class \"%s\" failed.\n", cgutil_vaddr[cls]->grpname); - return -1; - } - - /* - * step 4: update the belonging workload groups. - * 0 means no group need be ignored in the reseting list - */ - if (cgexec_reset_cpuset_cgroups(cls, 0) == -1) { - fprintf(stderr, "ERROR: reset workload cpuset for class \"%s\" failed.\n", cgutil_vaddr[cls]->grpname); - return -1; - } - cgutil_vaddr[cls]->ainfo.quota = cgutil_opt.setspct; - - } else if (cgutil_opt.setfixed) { - if (cgexec_update_class_cpuset(cls, cgutil_vaddr[TOPCG_CLASS]->cpuset) == -1) - return -1; - - if (cgexec_reset_cpuset_cgroups(cls, 0) == -1) { - fprintf(stderr, "ERROR: reset workload cpuset for class \"%s\" failed.\n", cgutil_vaddr[cls]->grpname); - return -1; - } - - cgutil_vaddr[cls]->ainfo.quota = 0; - } - } else { - fprintf(stderr, "ERROR: the specified class group %s doesn't exist!\n", cgutil_opt.clsname); - return -1; - } - - return 0; -} - -/* - * function name: cgexec_update_fixed_backend_cgroup - * description : update the cpuset value of Backend group by user set "--fixed" - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_fixed_backend_cgroup(void) -{ - int i, bkd = 0; - char cpuset[CPUSET_LEN]; - int need_reset = 0; - - /* check if the class exists */ - for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0) - continue; - - if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.bkdname)) { - bkd = i; - break; - } - } - - if (bkd) { - /* set cpuset by percentage*/ - if (cgutil_opt.setspct) { - /* the same steps with updating workload groups.*/ - if ((need_reset = cgexec_check_cpuset_percent(TOPCG_BACKEND, bkd, cpuset)) == -1) - return -1; - - if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(TOPCG_BACKEND, bkd) == -1)) - return -1; - - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[bkd], cpuset) == -1) - return -1; - - cgutil_vaddr[bkd]->ainfo.quota = cgutil_opt.setspct; - } else if (cgutil_opt.setfixed) { - if (cgexec_update_cgroup_cpuset(cgutil_vaddr[bkd], cgutil_vaddr[TOPCG_BACKEND]->cpuset) == -1) - return -1; - - cgutil_vaddr[bkd]->ainfo.quota = 0; - } - } else { - fprintf(stderr, "ERROR: the specified backend group %s doesn't exist!\n", cgutil_opt.bkdname); - return -1; - } - - return 0; -} - -/* - * function name: cgexec_update_fixed_top_cgroup - * description : update cpuset of Top group by user set "--fixed" - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_update_fixed_top_cgroup(void) -{ - char cpusets[CPUSET_LEN]; - int need_reset = 0; - int top = 0; - - if (0 == strcmp(cgutil_opt.topname, GSCGROUP_ROOT)) { - fprintf(stderr, "ERROR: users can't modify the Root cgroup with \"--fixed\"!\n"); - return -1; - } else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE) || - 0 == strcmp(cgutil_opt.topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname)) { - top = TOPCG_GAUSSDB; - if (cgutil_opt.setspct) { - fprintf(stderr, "ERROR: users can't modify the cpu cores percentage of Gaussdb cgroup with \"--fixed\"!\n"); - return -1; - } - } else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_BACKEND)) - top = TOPCG_BACKEND; - else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_CLASS)) - top = TOPCG_CLASS; - else { - fprintf(stderr, "ERROR: the specified top group %s doesn't exist!\n", cgutil_opt.topname); - return -1; - } - - if (top) { - if (cgutil_opt.setspct) { - if ((need_reset = cgexec_check_cpuset_percent(TOPCG_GAUSSDB, top, cpusets)) == -1) - return -1; - - if (need_reset && (cgexec_reset_cpuset_cgroups(TOPCG_GAUSSDB, top) == -1)) - return -1; - - if (cgexec_update_top_group_cpuset(top, cpusets) == -1 || cgexec_reset_cpuset_cgroups(top, 0) == -1) - return -1; - - cgutil_vaddr[top]->ainfo.quota = cgutil_opt.setspct; - } else if (cgutil_opt.setfixed) { - if (cgexec_update_top_group_cpuset(top, cgutil_vaddr[TOPCG_GAUSSDB]->cpuset) == -1 || - cgexec_reset_cpuset_cgroups(top, 0) == -1) - return -1; - - cgutil_vaddr[top]->ainfo.quota = 0; - } - } - - return 0; -} - -/* - **************** EXTERNAL FUNCTION ******************************** - */ - -/* - * function name: cgexec_check_SLESSP2_version - * description : check if the current OS version is SLES SP2 - * return value : - * 1: is the sles sp2 - * 0: is not the sles sp2, supposed as sles sp1 - * -1: abnormal - * - * Note: Search "io" column in /proc/cgroups. - * It need to check the value if the next release supports - * Redhat or Euler version. - */ -int cgexec_check_SLESSP2_version(void) -{ - char buf[PROCLINE_LEN]; - FILE* f = NULL; - - f = fopen("/proc/cgroups", "r"); - - if (f == NULL) - return -1; - - while (NULL != fgets(buf, PROCLINE_LEN, f)) { - /* example from proc: - * #subsys_name hierarchy num_cgroups enabled - * cpu 0 1 1 - * - * search "blkio" column - */ - - if (strstr(buf, MOUNT_BLKIO_NAME) != NULL) { - cgutil_is_sles11_sp2 = 1; - fclose(f); - return 1; - } - } - - fclose(f); - return 0; -} - -/* - * @Description: check whether execute upgrade. - * @IN void - * @Return: 1: upgrade 0: not upgrade - * @See also: - */ -int cgexec_check_mount_for_upgrade(void) -{ - int i, ret, old_mp = 0; - - errno_t sret; - - /* Only root user can do upgrade */ - if (geteuid() != 0) - return 0; - - /* - * if cpuset and cpuacct has not mounted, we will check whether - * cpu or blkio is mounted on default point, if yes, we must unmount - * them firstly, and then mount all sub system with new mount point. - * if no system in default point, we need not umount them. - */ - for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - /* ignore blkio and memory. */ - if (i == MOUNT_BLKIO_ID || i == MOUNT_MEMORY_ID) - continue; - - if (*cgutil_opt.mpoints[i]) { - if (strcmp(cgutil_opt.mpoints[i], GSCGROUP_MOUNT_POINT_OLD) == 0) { - char fname[256]; - int cnt = 0; - struct dirent* file = NULL; - DIR* dir = opendir(GSCGROUP_MOUNT_POINT_OLD); - - if (dir == NULL) { - fprintf(stderr, "ERROR: failed to open %s.\n", GSCGROUP_MOUNT_POINT_OLD); - return -1; - } - - sret = snprintf_s( - fname, sizeof(fname), sizeof(fname) - 1, "%s:%s", "Gaussdb", cgutil_passwd_user->pw_name); - securec_check_intval(sret, closedir(dir), -1); - - /* if other user has created cgroup in the default mout point, we cannot unmount the point. */ - while ((file = readdir(dir)) != NULL) { - if (file->d_type != DT_DIR || strcmp(file->d_name, fname) == 0) - continue; - - if (file->d_type == DT_DIR && strncmp(file->d_name, "Gaussdb:", 8) == 0) - ++cnt; - } - - closedir(dir); - - if (cnt > 0) { - fprintf(stderr, - "ERROR: The other user has cgroups in \"%s\", upgrade failed.\n", - GSCGROUP_MOUNT_POINT_OLD); - return -1; - } - - old_mp++; - } - } - } - - /* if cgroups are mounted on old path, only umount once time */ - if (old_mp) { - char cmd[128]; - - /* more than one cgroups have been mounted under /dev/cgroups */ - if (old_mp > 1) { - (void)cgroup_init(); /* init first */ - (void)cgptree_drop_cgroups(); - } - - sret = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", GSCGROUP_MOUNT_POINT_OLD); - securec_check_intval(sret, , -1); - - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", GSCGROUP_MOUNT_POINT_OLD); - return -1; - } - - /* get new mount points and mount them */ - (void)cgexec_get_mount_points(); - (void)cgexec_mount_root_cgroup(); - } else - cgutil_opt.upgrade = 0; - - return 0; -} - -/* - * @Description: get all cgroup sub system's mount points. - * @IN void - * @Return: 0: normal -1: abnormal - * @See also: - */ -int cgexec_get_mount_points(void) -{ - struct mntent* ent = NULL; - char mntent_buffer[5 * FILENAME_MAX]; - - struct mntent temp_ent; - int i; - - errno_t rc; - rc = memset_s(&temp_ent, sizeof(temp_ent), 0, sizeof(temp_ent)); - securec_check_errno(rc, , -1); - - /* reset mount points */ - rc = memset_s(cgutil_opt.mpoints, MOUNT_SUBSYS_KINDS * MAXPGPATH, 0, MOUNT_SUBSYS_KINDS * MAXPGPATH); - securec_check_errno(rc, , -1); - - /* open '/proc/mounts' to load mount points */ - FILE* proc_mount = fopen("/proc/mounts", "re"); - - if (proc_mount == NULL) - return -1; - - while ((ent = getmntent_r(proc_mount, &temp_ent, mntent_buffer, sizeof(mntent_buffer))) != NULL) { - /* not cgroup, pass */ - if (strcmp(ent->mnt_type, "cgroup") != 0) - continue; - - for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - if (hasmntopt(ent, cgutil_subsys_table[i]) == NULL) - continue; - - /* get mount point */ - rc = snprintf_s(cgutil_opt.mpoints[i], - sizeof(cgutil_opt.mpoints[i]), - sizeof(cgutil_opt.mpoints[i]) - 1, - "%s", - ent->mnt_dir); - securec_check_intval(rc, fclose(proc_mount), -1); - } - } - - fclose(proc_mount); - - return 0; -} - -/* - * @Description: detect if cgroup file system has been mounted. - * @IN void - * @Return: 1: has been mounted on the specified directory - * 0: hasn't been mounted - * -1: has been mounted on other directory - * @See also: - */ -int cgexec_detect_cgroup_mount(void) -{ - int i, j; - - if (cgutil_opt.cflag <= 0) { - return 1; - } - - for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - if (i == MOUNT_BLKIO_ID) - continue; - - /* a subsys has not mounted, we must make sure its mount point is valid. */ - if (*cgutil_opt.mpoints[i] == '\0') { - if (cgutil_opt.mpflag == 0) { - /* no new mount point, make sure default point is valid */ - for (j = 0; j < MOUNT_SUBSYS_KINDS; ++j) - if (strcmp(cgutil_opt.mpoints[j], GSCGROUP_MOUNT_POINT) == 0) - return -1; - } - - return 0; - } - } - - return 1; -} - -static int RemoveExistSymbolLink(const char* mpoint) -{ - int ret; - char cmd[MAX_COMMAND_LENGTH]; - struct stat statbuf; - errno_t rc; - - rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); - securec_check_c(rc, "\0", "\0"); - /* If the mount point is already exist, directory should be remove */ - ret = lstat(mpoint, &statbuf); - if (S_ISLNK(statbuf.st_mode)) { - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "rm %s", mpoint); - securec_check_ss_c(rc, "\0", "\0"); - - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to remove exist symbol link %s!\n", mpoint); - return -1; - } - } - return 0; -} - -static int MountCgroupInternal(const char* mpoint, const char* type) -{ - int ret; - char cmd[MAX_COMMAND_LENGTH]; - struct stat statbuf; - errno_t rc; - - rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); - securec_check_c(rc, "\0", "\0"); - /* check new mount point directory */ - ret = stat(mpoint, &statbuf); - if (ret != 0 || !S_ISDIR(statbuf.st_mode)) { - if (mkdir(mpoint, S_IRWXU) != 0) { - fprintf(stderr, "ERROR: failed to create %s directory!\n", mpoint); - return -1; - } - } - - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, - "mount -t cgroup -o %s %s %s", type, type, mpoint); - securec_check_ss_c(rc, "\0", "\0"); - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to mount cgroup under %s!\n", mpoint); - return -1; - } - fprintf(stderr, "LOG: mount %s success.\n", type); - return 0; -} - -static int LinkCpuCgroup(const char* target, const char* source) -{ - int ret; - char cmd[MAX_COMMAND_LENGTH]; - errno_t rc; - - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "ln -s %s %s", source, target); - securec_check_ss_c(rc, "\0", "\0"); - - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to mount cgroup under %s!\n", target); - return -1; - } - - return 0; -} - -static int CgexecRemountCpuCgroup(const char* path, const char* tmp_mpoint) -{ - int ret; - char mpoint[MOUNT_POINT_LENGTH]; - errno_t rc; - - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, - "%s/cpu", path); - securec_check_ss_c(rc, "\0", "\0"); - ret = RemoveExistSymbolLink(mpoint); - if (ret != 0) { - return ret; - } - ret = LinkCpuCgroup(mpoint, tmp_mpoint); - if (ret != 0) { - return ret; - } - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, - "%s/cpuacct", path); - securec_check_ss_c(rc, "\0", "\0"); - ret = RemoveExistSymbolLink(mpoint); - if (ret != 0) { - return ret; - } - ret = LinkCpuCgroup(mpoint, tmp_mpoint); - - return ret; -} - -static int CgexecMountCpuCgroup(const char* path) -{ - int ret; - char tmp_mpoint[MOUNT_POINT_LENGTH]; - char mpoint[MOUNT_POINT_LENGTH]; - errno_t rc; - - /* cpu and cpuacct sub-system should mount cpu,cpuacct sub-system */ - rc = snprintf_s(tmp_mpoint, sizeof(tmp_mpoint), sizeof(tmp_mpoint) - 1, - "%s/cpu,cpuacct", path); - securec_check_ss_c(rc, "\0", "\0"); - ret = MountCgroupInternal(tmp_mpoint, "cpu,cpuacct"); - if (ret != 0) { - /* mount failed means that cpu and cpuacct not mount together */ - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, - "%s/cpu", path); - securec_check_ss_c(rc, "\0", "\0"); - ret = MountCgroupInternal(mpoint, cgutil_subsys_table[MOUNT_CPU_ID]); - if (ret != 0) { - return ret; - } - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, - "%s/cpuacct", path); - securec_check_ss_c(rc, "\0", "\0"); - ret = MountCgroupInternal(mpoint, cgutil_subsys_table[MOUNT_CPUACCT_ID]); - } else { - ret = CgexecRemountCpuCgroup(path, tmp_mpoint); - } - - return ret; -} - -/* - * @Description: mount the Cgroup file system on the Root directory. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_mount_root_cgroup(void) -{ - int i, ret; - - char mpoint[MOUNT_POINT_LENGTH]; - char* path = NULL; - struct stat statbuf; - - errno_t rc; - rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); - securec_check_c(rc, "\0", "\0"); - - if (cgutil_opt.mpflag) - path = cgutil_opt.mpoint; - else - path = GSCGROUP_MOUNT_POINT; - - if (CheckBackendEnv(path) != 0) { - return -1; - } - /* Create mount point directory */ - ret = stat(path, &statbuf); - if (0 != ret || !S_ISDIR(statbuf.st_mode)) { - if (mkdir(path, S_IRWXU | (S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != 0) { - fprintf(stderr, "ERROR: failed to create %s directory!\n", path); - return -1; - } - /* change the right to 755 */ - (void)chmod(path, S_IRWXU | (S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); - } - - for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - /* 'blkio' is invalid, ignore it */ - if (i == MOUNT_BLKIO_ID) - continue; - - /* If the subsys has not mounted, we will use new point to mount. */ - if (*cgutil_opt.mpoints[i] == '\0') { - /* cpu and cpuacct sub-system all mount on cpu,cpuacct in new linux system */ - if (i == MOUNT_CPU_ID) { - ret = CgexecMountCpuCgroup(path); - i++; - } else { - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/%s", path, cgutil_subsys_table[i]); - securec_check_ss_c(rc, "\0", "\0"); - ret = MountCgroupInternal(mpoint, cgutil_subsys_table[i]); - } - } - } - - return 0; -} - -static int CgexecUmountRootCgroupInternal(const char* path, int index) -{ - int ret; - char cmd[MAX_COMMAND_LENGTH], mpoint[MOUNT_POINT_LENGTH]; - errno_t rc; - - /* get mount point full name */ - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/%s", path, cgutil_subsys_table[index]); - securec_check_ss_c(rc, "\0", "\0"); - - /* we will unmount the point which you specify. */ - if (strcmp(cgutil_opt.mpoints[index], mpoint) == 0) { - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", mpoint); - securec_check_ss_c(rc, "\0", "\0"); - - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", mpoint); - return -1; - } - fprintf(stderr, "LOG: umount cgroup under %s!\n", mpoint); - } else if (index == MOUNT_CPU_ID || index == MOUNT_CPUACCT_ID) { - /* check new mount point directory */ - RemoveExistSymbolLink(mpoint); - - if (index == MOUNT_CPU_ID) { - rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/cpu,cpuacct", path); - securec_check_ss_c(rc, "\0", "\0"); - if (*cgutil_opt.mpoints[index] && strcmp(cgutil_opt.mpoints[index], mpoint) == 0) { - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", mpoint); - securec_check_ss_c(rc, "\0", "\0"); - - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", mpoint); - return -1; - } - } - } - } - - return 0; -} - -/* - * @Description: umount the Cgroup file system. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_umount_root_cgroup(void) -{ - int i, ret; - char cmd[MAX_COMMAND_LENGTH]; - char* path = NULL; - errno_t rc; - - if (cgutil_opt.mpflag) - path = cgutil_opt.mpoint; - else - path = GSCGROUP_MOUNT_POINT; - - if (CheckBackendEnv(path) != 0) { - return -1; - } - for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - /* 'blkio' is invalid, ignore it */ - if (i == MOUNT_BLKIO_ID) - continue; - - if (*cgutil_opt.mpoints[i] == '\0') { - continue; - } - - /* It has mounted on old default point, we unmount it only once. */ - if (strcmp(cgutil_opt.mpoints[i], GSCGROUP_MOUNT_POINT_OLD) == 0) { - rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", GSCGROUP_MOUNT_POINT_OLD); - securec_check_ss_c(rc, "\0", "\0"); - - ret = system(cmd); - if (CheckSystemSucess(ret) == -1) { - fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", GSCGROUP_MOUNT_POINT_OLD); - return -1; - } - - break; - } - - ret = CgexecUmountRootCgroupInternal(path, i); - } - - return 0; -} - -/* - * function name: cgexec_delete_cgroups - * description : delete the Cgroup based on relative path - * arguments : - * relpath: the relative path - * return value : - * -1: abnormal - * 0: normal - * - * Note: the function is used when dropping a Cgroup. - */ -int cgexec_delete_cgroups(char* relpath) -{ - int ret; - struct cgroup* cg = NULL; - - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); - return -1; - } - - /* get all information regarding the cgroup from kernel */ - ret = cgroup_get_cgroup(cg); - if (ret != 0) { - fprintf( - stdout, "ERROR: failed to get '%s' cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); - cgroup_free(&cg); - return -1; - } - - (void)cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_RECURSIVE | CGFLAG_DELETE_IGNORE_MIGRATION); - - cgroup_free(&cg); - - return 0; -} - -/* - * function name: cgexec_create_groups - * description : main entry of create Cgroup; - * When the user is root, it needs to check if Cgroups have - * been created. If it didn't, the default Cgroups will be created. - * When the user is non-root user and the Cgroups didn't exist, - * it needs to report an error. - * Only Class and Workload Cgroup can be created, but it doesn't - * allow to create workload Cgroup for DefaultClass Cgroup. - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_create_groups(void) -{ - int cgcnt; - struct stat buf; - int ret; - size_t len = sizeof(cgutil_opt.mpoint) + 1 + sizeof(GSCGROUP_TOP_DATABASE) + 1 + USERNAME_LEN + 1 + - sizeof(GSCGROUP_TOP_CLASS); - char* cgpath = (char*)malloc(len); - errno_t sret; - - if (cgpath == NULL) - return -1; - - sret = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); - securec_check_errno(sret, free(cgpath), -1); - - if (geteuid() == 0) { - /* create cm cgroup, this function could check if directory exists. - * So we need not check it firstly. - */ - (void)cgexec_create_cm_default_cgroup(); - - cgcnt = cgexec_get_cgroup_number(); - if (1 == cgcnt) { - /* create the default cgroups */ - (void)cgexec_create_default_cgroups(); - } else { - /* check if cgroups have been created for the specified user */ - sret = sprintf_s(cgpath, - len, - "%s/%s:%s/%s", - cgutil_opt.mpoints[MOUNT_CPU_ID], - GSCGROUP_TOP_DATABASE, - cgutil_opt.user, - GSCGROUP_TOP_CLASS); - securec_check_intval(sret, free(cgpath), -1); - - ret = stat(cgpath, &buf); - - if (0 != ret) - (void)cgexec_create_default_cgroups(); - } - - /* default class has no exception data, we will set a default one */ - if (gsutil_exception_is_valid(cgutil_vaddr[CLASSCG_START_ID], EXCEPT_ALL_KINDS) == 0) { - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].skewpercent = DEFAULT_CPUSKEWPCT; - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].qualitime = DEFAULT_QUALITIME; - } - } else { - /* check if cgroups have been created for the specified user */ - sret = sprintf_s(cgpath, - len, - "%s/%s:%s/%s", - cgutil_opt.mpoints[MOUNT_CPU_ID], - GSCGROUP_TOP_DATABASE, - cgutil_passwd_user->pw_name, - GSCGROUP_TOP_CLASS); - securec_check_intval(sret, free(cgpath), -1); - - ret = stat(cgpath, &buf); - - if (0 != ret) { - fprintf(stderr, - "ERROR: There are no cgroups for %s! Please remount it by root.\n", - cgutil_passwd_user->pw_name); - free(cgpath); - cgpath = NULL; - return -1; - } - } - - /* create nodegroup info */ - if (cgutil_opt.nodegroup[0]) { - size_t nglen = sizeof(cgutil_opt.mpoint) + 1 + sizeof(GSCGROUP_TOP_DATABASE) + 1 + USERNAME_LEN + 1 + - strlen(cgutil_opt.nodegroup); - char* ngcgpath = (char*)malloc(nglen); - - if (ngcgpath == NULL) { - free(cgpath); - cgpath = NULL; - return -1; - } - - /* check if cgroups have been created for the specified nodegroup */ - sret = sprintf_s(ngcgpath, - nglen, - "%s/%s:%s/%s", - cgutil_opt.mpoints[MOUNT_CPU_ID], - GSCGROUP_TOP_DATABASE, - cgutil_passwd_user->pw_name, - cgutil_opt.nodegroup); - securec_check_intval(sret, free(cgpath); free(ngcgpath), -1); - - ret = stat(ngcgpath, &buf); - /* if the nodegroup doesn't exist, create the default cgroups */ - if (0 != ret) { - /* rename the origin cgroup into nodegroup cgroup */ - if (cgutil_opt.rename) { - /* delete old cgroups */ - (void)cgptree_drop_nodegroup_cgroups(GSCGROUP_TOP_CLASS); - } - - /* create nodegroup default cgroups by mapping info */ - if (-1 == cgexec_create_nodegroup_default_cgroups()) { - free(ngcgpath); - ngcgpath = NULL; - free(cgpath); - cgpath = NULL; - return -1; - } - - if (cgutil_opt.rename) { - void* vaddr = cgconf_map_nodegroup_conffile(); - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - - if (NULL == vaddr) { - fprintf(stderr, "ERROR: node group config file is removed during rename!"); - free(ngcgpath); - ngcgpath = NULL; - free(cgpath); - cgpath = NULL; - return -1; - } - - for (int i = 0; i < CLASSCG_START_ID; i++) { - gscgroup_grp_t* tmpvaddr = (gscgroup_grp_t*)vaddr + i; - sret = memcpy_s(cgutil_vaddr[i], sizeof(gscgroup_grp_t), tmpvaddr, sizeof(gscgroup_grp_t)); - securec_check_errno(sret, (void)munmap(vaddr, cglen); free(cgpath); free(ngcgpath);, -1); - } - - /* unmap the vaddr for default group */ - (void)munmap(vaddr, GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - - /* unmap the vaddr for default group */ - (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - - /* parse the origin group for nodegroup */ - if (-1 == cgconf_parse_nodegroup_config_file()) { - free(ngcgpath); - ngcgpath = NULL; - free(cgpath); - cgpath = NULL; - return -1; - } - - current_nodegroup = cgutil_opt.nodegroup; - - /* create nodegroup default cgroups by mapping info */ - if (-1 == cgexec_create_nodegroup_default_cgroups()) { - free(ngcgpath); - ngcgpath = NULL; - free(cgpath); - cgpath = NULL; - return -1; - } - } - } - - free(ngcgpath); - ngcgpath = NULL; - } - - free(cgpath); - cgpath = NULL; - - /* create Class group */ - if (cgutil_opt.clsname[0]) { - if (0 == strcmp(cgutil_opt.clsname, GSCGROUP_DEFAULT_CLASS)) { - fprintf(stderr, - "ERROR: Can't create Default Workload Cgroup " - "for DefaultClass Cgroup!\n"); - return -1; - } - - /* create this class cgroup */ - if (-1 == cgexec_create_class_cgroup()) { - cgconf_remove_backup_conffile(); - } - } - - return 0; -} - -/* - * function name: cgexec_drop_nodegroup_cgroups - * description : drop cgroups of the specified nodegroup - * - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_drop_nodegroup_cgroups(void) -{ - /* delete the configure file */ - char* cfgpath = NULL; - - cfgpath = cgconf_get_config_path(false); - - if (cfgpath != NULL && !cgutil_opt.rename) { - (void)unlink(cfgpath); - } else if (cfgpath == NULL) { - return -1; - } - - /* remove unused backup file */ - cgconf_remove_backup_conffile(); - - /* drop the nodegroup Cgroup tree */ - (void)cgptree_drop_nodegroup_cgroups(cgutil_opt.nodegroup); - - if (cgutil_opt.rename) { - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - errno_t sret; - - /* delete old cgroups */ - (void)cgptree_drop_nodegroup_cgroups(GSCGROUP_TOP_CLASS); - - /* map the original Cgroup Configuration file */ - vaddr = cgconf_map_origin_conffile(); - if (NULL == vaddr) { - fprintf(stderr, "ERROR: failed to create and map the configure file!\n"); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - for (int i = 0; i < CLASSCG_START_ID; i++) { - gscgroup_grp_t* tmpvaddr = (gscgroup_grp_t*)vaddr + i; - sret = memcpy_s(cgutil_vaddr[i], sizeof(gscgroup_grp_t), tmpvaddr, sizeof(gscgroup_grp_t)); - securec_check_errno(sret, (void)munmap(vaddr, cglen); free(cfgpath);, -1); - } - - /* unmap the vaddr for default group */ - (void)munmap(vaddr, GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - - current_nodegroup = NULL; - - /* create nodegroup default cgroups by mapping info */ - if (-1 == cgexec_create_nodegroup_default_cgroups()) { - free(cfgpath); - cfgpath = NULL; - return -1; - } - - cgutil_opt.nodegroup[0] = '\0'; // reset the node group info - - /* rename the configuration file */ - char* old_confpath = cgconf_get_config_path(false); - if (NULL == old_confpath) { - fprintf(stderr, "ERROR: failed to get the configuration path,configuration path is NULL."); - free(cfgpath); - cfgpath = NULL; - return -1; - } - - if (-1 == rename(cfgpath, old_confpath)) { - fprintf(stderr, "ERROR: failed to rename %s to %s.", cfgpath, old_confpath); - free(cfgpath); - cfgpath = NULL; - free(old_confpath); - old_confpath = NULL; - return -1; - } - free(old_confpath); - old_confpath = NULL; - } - - free(cfgpath); - cfgpath = NULL; - return 0; -} - -/* - * function name: cgexec_drop_groups - * description : when there is no specified Class group, root user will - * delete Gaussdb group. Otherwise, it will delete - * the Class name. When "-M" option is specified, it umounts - * cgroup file system. - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_drop_groups(void) -{ - /* root user drops common user's cgroups and umount */ - if ('\0' != cgutil_opt.user[0] && geteuid() == 0 && '\0' == cgutil_opt.clsname[0]) { - (void)cgptree_drop_cgroups(); - } - - /* root user drops cm cgroup */ - if ('\0' != cgutil_opt.user[0] && geteuid() == 0) { - (void)cgexec_delete_cm_cgroup(); - } - - /* root user drops common user's cgroups and umount */ - if (geteuid() != 0 && '\0' != cgutil_opt.nodegroup[0] && '\0' == cgutil_opt.clsname[0]) { - (void)cgexec_drop_nodegroup_cgroups(); - } - - if ('\0' != cgutil_opt.clsname[0]) { - if (0 == strcmp(cgutil_opt.clsname, GSCGROUP_DEFAULT_CLASS)) { - fprintf(stderr, "ERROR: Can't drop DefaultClass Cgroup!\n"); - return -1; - } - - if (-1 == cgexec_delete_class_cgroup()) { - cgconf_remove_backup_conffile(); - } - } - - if (geteuid() == 0 && cgutil_opt.umflag) { - fprintf(stdout, "ERROR: Cgroup is mounted. Ready to umount cgroup!\n"); - - /* mount the cgroup */ - (void)cgexec_umount_root_cgroup(); - } - - return 0; -} - -/* - * function name: cgexec_update_groups - * description : update the dynamic value or fixed value based on options - * return value : - * -1: abnormal - * 0: normal - * - */ -int cgexec_update_groups(void) -{ - int ret = 0; - - /* back up the config file */ - if (-1 == cgconf_backup_config_file()) - return -1; - - if (0 == cgutil_opt.fixed) { - if ('\0' != cgutil_opt.clsname[0]) - ret = cgexec_update_dynamic_class_cgroup(); - - if ('\0' != cgutil_opt.bkdname[0]) - ret = cgexec_update_dynamic_backend_cgroup(); - - if ('\0' != cgutil_opt.topname[0]) - ret = cgexec_update_dynamic_top_cgroup(); - } else if (cgutil_opt.fixed) { - if ('\0' != cgutil_opt.clsname[0]) - ret = cgexec_update_fixed_class_cgroup(); - - if ('\0' != cgutil_opt.bkdname[0]) - ret = cgexec_update_fixed_backend_cgroup(); - - if ('\0' != cgutil_opt.topname[0]) - ret = cgexec_update_fixed_top_cgroup(); - } - - /* remove the backup file */ - if (-1 == ret) { - cgconf_remove_backup_conffile(); - } - - return 0; -} - -/* get Root information */ -int cgexec_get_cgroup_cpuset_info(int cnt, char** cpuset) -{ - char* relpath = NULL; - struct cgroup* cg = NULL; - struct cgroup_controller* cgc_cpu = NULL; - int ret; - - /* get the relative path */ - if (NULL == (relpath = gscgroup_get_relative_path(cnt, cgutil_vaddr, current_nodegroup))) - return -1; - - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); - free(relpath); - relpath = NULL; - return -1; - } - - /* get all information regarding the cgroup from kernel */ - ret = cgroup_get_cgroup(cg); - if (ret != 0) { - fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); - cgroup_free(&cg); - free(relpath); - relpath = NULL; - return -1; - } - - free(relpath); - relpath = NULL; - - /* get the CPU controller */ - cgc_cpu = cgroup_get_controller(cg, MOUNT_CPUSET_NAME); - if (NULL == cgc_cpu) { - fprintf(stderr, "ERROR: failed to add %s controller in %d!\n", MOUNT_CPU_NAME, cnt); - cgroup_free(&cg); - return -1; - } - - /* get cpuset value with controller */ - if (0 != (ret = cgroup_get_value_string(cgc_cpu, CPUSET_CPUS, cpuset))) { - fprintf(stderr, "ERROR: failed to get %s for %s\n", CPUSET_CPUS, cgroup_strerror(ret)); - goto error; - } - - /* Get the mems info */ - if (cnt == TOPCG_ROOT) { - char* cpumems = NULL; - if (0 != (ret = cgroup_get_value_string(cgc_cpu, CPUSET_MEMS, &cpumems))) { - fprintf(stderr, "ERROR: failed to get %s for %s\n", CPUSET_MEMS, cgroup_strerror(ret)); - goto error; - } - - errno_t sret = snprintf_s(cgutil_mems, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpumems); - securec_check_intval(sret, free(cpumems); cgroup_free_controllers(cg); cgroup_free(&cg), -1); - - free(cpumems); - cpumems = NULL; - } - - cgroup_free_controllers(cg); - cgroup_free(&cg); - return 0; -error: - cgroup_free_controllers(cg); - cgroup_free(&cg); - return -1; -} - -/* - * @Description: get current memory count. - * @OUT mems: memory set - * @IN size: memory set size - * @Return: memory set - * @See also: - */ -char* cgexec_get_cgroup_cpuset_mems(char* mems, int size) -{ - int ret = 1; - char cmd[128]; - char line[128]; - - FILE* fp = NULL; - - /* open '/proc/cpuinf' to search 'physical id' count to get memory set */ - errno_t sret = snprintf_s(cmd, - sizeof(cmd), - sizeof(cmd) - 1, - "%s", - "lscpu | grep \"NUMA node(s)\" | awk -F: '{print $2}'| sed 's/\\ //g'"); - securec_check_intval(sret, , mems); - - if ((fp = popen(cmd, "r")) != NULL) { - if (fgets(line, sizeof(line), fp) != NULL) { - /* get count */ - ret = atoi(line); - - if (ret == 0) - ret = 1; - } - - pclose(fp); - } - - /* get memory set */ - sret = snprintf_s(mems, size, size - 1, "%d-%d", 0, ret - 1); - securec_check_intval(sret, , mems); - - return mems; -} - -/* - * @Description: update config cpuset - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_check_top_cpuset(void) -{ - /* - * Check whether the maximum number of configuration file is - * compatible with the total number of cores in the current - * node. If compatible, no update is required - */ - if (cgexec_check_cpuset_value(cgutil_allset, cgutil_vaddr[TOPCG_GAUSSDB]->cpuset) == 0) - return 0; - - /* not compatible, we have to update the configuration file */ - for (int i = 0; i <= WDCG_END_ID; ++i) { - if (cgutil_vaddr[i]->used == 0) - continue; - - errno_t sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); - securec_check_intval(sret, , -1); - } - - return 0; -} - -/* - * @Description : update cpu core percentage and cpusets values recursively. - * : the previous "-f" is replaced by "--fixed", so - * : groups with "cpusets" are all - * : transfered to percentage of the high level - * @IN high : high level group id. - * @IN extended : the total quota is out of range, so cpusets of the groups - * : and the belonging groups are all the same with the - * : high level groups, except those who has been set "quota" - * : already. - * @Return : - * @See also: - */ -static void cgexec_update_fixed_config(int high, int extended) -{ - int forstart = 0, forend = 0; /* start and end value of the loop */ - int i = 0; - int lowlen = 0, highlen = 0; /* low and high level cpuset length */ - int start = 0, end = 0; /* only used to call function cgexec_get_cpuset_length*/ - int lowstart = 0, lowend = 0; /* low group cpuset start and end value */ - int highstart = 0, highend = 0; /* high group cpuset start and end value */ - int part_quota = 0; /* the current quota transfered from cpusets */ - int sum_quota = 0; /* sum of the quota values */ - errno_t sret = 0; /* securec_check return value */ - char sets[CPUSET_LEN]; /* the calculated cpuset to be updated */ - bool flag = false; /*flag to indicate first time enter the loop */ - char topwd[GPNAME_LEN]; - - /* get 'topwd' cgroup full name*/ - sret = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); - securec_check_intval(sret, , ); - - /* if high has been the lowest level, the recursion is interrupted */ - if (cgexec_get_cgroup_id_range(high, &forstart, &forend) == -1) - return; - - /* the cpuset length of the high level group */ - highlen = cgexec_get_cpuset_length(cgutil_vaddr[high]->cpuset, &highstart, &highend); - - sum_quota = cgexec_check_fixed_percent(high); - - for (i = forstart; i <= forend; i++) { - /* the low level group is ignored in the reseting list */ - if (cgutil_vaddr[i]->used == 0) - continue; - - /* only the workload groups belonging to high class is considered */ - if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && (high != cgutil_vaddr[i]->ginfo.wd.cgid)) - continue; - - /* transfer cpuset to quota */ - if (!cgutil_vaddr[i]->ainfo.quota) { - /* get length of the low level cpuset */ - if (*cgutil_vaddr[i]->cpuset != '\0') - lowlen = cgexec_get_cpuset_length(cgutil_vaddr[i]->cpuset, &start, &end); - - /* quota is still set to 0 */ - if (lowlen == highlen || !lowlen || extended || - (strcmp(cgutil_vaddr[i]->grpname, topwd) == 0 && (i >= WDCG_START_ID) && (i <= WDCG_END_ID))) { - sret = - snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[high]->cpuset); - securec_check_intval(sret, , ); - - /* update the configure recursively */ - if (i < WDCG_START_ID) - cgexec_update_fixed_config(i, extended); - - /* extended is only available for the belonging groups of the current group */ - if (extended) - extended = 0; - - continue; - } - - /* transfer cpuset to quota */ - part_quota = cgexec_trans_cpusets_to_percent(highlen, lowlen); - - /* the new quota plus the total quota is out of range */ - if (part_quota + sum_quota > GROUP_ALL_PERCENT) { - /* - * the belonging groups will be marked as extended, - * and the cpusets will be the same with high level ones. - */ - extended = 1; - - sret = - snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[high]->cpuset); - securec_check_intval(sret, , ); - } else { - /* alter the quota configure for the successfully transfered groups */ - sum_quota += part_quota; - cgutil_vaddr[i]->ainfo.quota = part_quota; - } - } - - /* reset the configure file for all groups with quota value */ - if (cgutil_vaddr[i]->ainfo.quota) { - /* the low level groups (same level groups with "low") cpu core length */ - lowlen = cgexec_trans_percent_to_cpusets(highlen, cgutil_vaddr[i]->ainfo.quota); - /* - * only the first time enter the loop, flag is false - * the cpu cores are allocated sequentially within high group cpu core range. - * the first group to be reset is allocated from "highstart", - * the next are allocated following the previous group "lowend" + 1 - */ - lowstart = flag ? (lowend + 1) : highstart; - lowend = lowstart + lowlen - 1; - - /* - * the previous steps guarantee the total quota not out of range, - * so here we only need check whether the left cpu cores are enough or not, - * and and the not enough cases will be handled in the same way with - * cgexec_check_cpuset_percent. - */ - if (lowend > highend) { - lowstart = highend - lowlen + 1; - lowend = highend; - } - - /* "sets" restore the cpuset to be reset*/ - cgexec_get_cpu_core_range(sets, lowstart, lowend); - - sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", sets); - securec_check_intval(sret, , ); - - /* next time enter the loop, flag will be true*/ - if (!flag) - flag = true; - } - - /* update the configure recursively */ - if (i < WDCG_START_ID) - cgexec_update_fixed_config(i, extended); - - /* extended is only available for the belonging groups of the current group */ - if (extended) - extended = 0; - } -} - -/* - * function name: cgexec_refresh_groups_internal - * description : refresh groups internal function - * return value : - * -1: abnormal - * 0: normal - * - */ -static int cgexec_refresh_groups_internal(void) -{ - // update quota and cpusets for all the control groups recursively - cgexec_update_fixed_config(TOPCG_GAUSSDB, 0); - - /* create default groups. - * if an error happened, then return -1. - */ - if (cgexec_create_default_cgroups()) { - return -1; - } - if (cgexec_create_cm_default_cgroup()) { - return -1; - } - - return 0; -} - -/* - * @Description: refresh cgroup with configure file. - * @IN size: void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_refresh_original_groups(void) -{ - /* delete backend and class group */ - for (int idx = TOPCG_BACKEND; idx <= TOPCG_CLASS; ++idx) { - if (cgutil_vaddr[idx]->used == 0) - continue; - - if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[idx])) - return -1; - } - - /* - * We have to check if the configured maximum number of cores can - * be used on the current node, and if not, we will try to update - * the configuration file for compatibility with the new node - */ - if (cgexec_check_top_cpuset() == -1) { - fprintf(stderr, "ERROR: update top cpuset error."); - return -1; - } - - /* create the default cgroups */ - return cgexec_refresh_groups_internal(); -} - -/* - * @Description: refresh cgroup with configure file of nodegroup. - * @IN size: void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_refresh_nodegroup_groups(void) -{ - /* delete logical cluster group */ - if (-1 == cgptree_drop_nodegroup_cgroups(cgutil_opt.nodegroup)) - return -1; - - /* create the default cgroups */ - return cgexec_create_nodegroup_default_cgroups(); -} - -/* - * @Description: refresh cgroup with configure file. - * @IN size: void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_refresh_groups(void) -{ - if ('\0' == cgutil_opt.nodegroup[0]) - return cgexec_refresh_original_groups(); - else - return cgexec_refresh_nodegroup_groups(); -} - -/* - * @Description: revert cgroup configure. - * @IN size: void - * @Return: -1: abnormal 0: normal - * @See also: - */ -int cgexec_revert_groups(void) -{ - for (int cls = CLASSCG_START_ID + 1; cls <= CLASSCG_END_ID; ++cls) { - if (cgutil_vaddr[cls]->used == 0) - continue; - - /* delete all class group except default class group */ - if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls])) - return -1; - - /* reset all group configure */ - cgconf_reset_class_group(cls); - } - - /* revert configure file */ - cgconf_revert_config_file(); - - /* create default groups. - * if an error happened, then return -1. - */ - if (cgexec_create_default_cgroups()) - return -1; - if (cgexec_create_cm_default_cgroup()) - return -1; - - return 0; -} - -/* - * @Description: check if changes happened on both groups - * @IN cur: current group - * @IN bak: backup group - * @Return: 1: updated 0:no updated - * @See also: - */ -int cgexec_check_update_groups(gscgroup_grp_t* cur, gscgroup_grp_t* bak) -{ - int offset = offsetof(gscgroup_grp_t, ainfo); - int size = sizeof(alloc_info_t); - - if (0 == memcmp((void*)((char*)cur + offset), (void*)((char*)bak + offset), size)) - return 0; - else - return 1; -} - -/* - * @Description: recover groups by updating the percent groups - * @IN id: the id of updated group - * @Return: - * -1: abnormal 0: normal - * @See also: - */ -int cgexec_recover_update_percent_groups(int id) -{ - errno_t sret; - - /* class group changed */ - if (id >= CLASSCG_START_ID && id <= CLASSCG_END_ID) { - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[id])) - return -1; - } else if (id >= WDCG_START_ID && id <= WDCG_END_ID) { - int cls = cgutil_vaddr_back[id]->ginfo.wd.cgid; - - /* need to update all class groups and their workload groups */ - sret = memcpy_s(cgutil_vaddr[cls], sizeof(gscgroup_grp_t), cgutil_vaddr_back[cls], sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , -1); - - /* update the os cgroups */ - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[id])) - return -1; - - /* need to update the workload group */ - sret = memcpy_s(cgutil_vaddr[id], sizeof(gscgroup_grp_t), cgutil_vaddr_back[id], sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , -1); - - /* update the remain group */ - if (-1 == cgexec_update_remain_cgroup(cgutil_vaddr[id], cls)) - return -1; - } - - return 0; -} - -/* - * @Description: recover groups by updating the fixed class groups - * @IN id: the id of updated group - * @IN cpuset: input string of cpuset - * @IN reverse: flag if it is reverse or not - * @Return: - * -1: abnormal 0: normal - * @See also: - */ -int cgexec_recover_update_fixed_class_group(int id, char* cpuset, int reverse) -{ - errno_t sret; - - if (!reverse) /* from down to up */ - { - /* copy the value into class group */ - sret = strcpy_s(cgutil_vaddr[id]->cpuset, CPUSET_LEN, cpuset); - securec_check_errno(sret, , -1); - - /* update the class group into cgroup fs */ - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[id])) - return -1; - - /* update the remain group */ - for (int j = 1; j <= cgutil_vaddr[id]->ginfo.cls.maxlevel; ++j) { - if (-1 == cgexec_update_remain_cgroup_cpuset_value(id, j, cpuset)) - return -1; - } - - /* update the timeshare group */ - if (-1 == cgexec_update_timeshare_cpuset(cgutil_vaddr[id], cpuset, 0)) - return -1; - - /* update the TopWD group */ - if (-1 == cgexec_update_topwd_cgroup_cpuset(id, cpuset)) - return -1; - } else { - /* update the timeshare group */ - if (-1 == cgexec_update_timeshare_cpuset(cgutil_vaddr[id], cpuset, 1)) - return -1; - - /* update the remain group */ - for (int j = cgutil_vaddr[id]->ginfo.cls.maxlevel; j >= 1; --j) { - if (-1 == cgexec_update_remain_cgroup_cpuset_value(id, j, cpuset)) - return -1; - } - - /* update the TopWD group */ - if (-1 == cgexec_update_topwd_cgroup_cpuset(id, cpuset)) - return -1; - - /* copy the value into class group */ - sret = strcpy_s(cgutil_vaddr[id]->cpuset, GPNAME_LEN, cpuset); - securec_check_errno(sret, , -1); - - /* update the class group into cgroup fs */ - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[id])) - return -1; - } - - return 0; -} - -/* - * @Description: recover groups by updating the quota groups - * @IN id: the id of updated group - * @Return: - * -1: abnormal 0: normal - * @See also: - */ -int cgexec_recover_update_quota_groups(int id) -{ - /* class group changed */ - if (id >= CLASSCG_START_ID && id <= CLASSCG_END_ID) { - /* reset the default value as Top Class group */ - for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0 || (i != id && cgutil_vaddr[i]->ainfo.quota == 0)) - continue; - - /* update the remain and timeshare group based on backup value */ - if (-1 == cgexec_recover_update_fixed_class_group(i, cgutil_vaddr[TOPCG_CLASS]->cpuset, 0)) - return -1; - } - - /* update all workload group */ - for (int j = WDCG_START_ID; j <= WDCG_END_ID; j++) { - if (cgutil_vaddr_back[j]->used == 0 || cgutil_vaddr_back[j]->ginfo.wd.wdlevel == 1) - continue; - - /* update the workload group */ - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[j])) - return -1; - } - - /* re-update the Class Group */ - for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { - if (cgutil_vaddr[i]->used == 0 || (i != id && cgutil_vaddr[i]->ainfo.quota == 0)) - continue; - - if (-1 == cgexec_recover_update_fixed_class_group(i, cgutil_vaddr_back[i]->cpuset, 1)) - return -1; - } - } else if (id >= WDCG_START_ID && id <= WDCG_END_ID) { - int cls = cgutil_vaddr_back[id]->ginfo.wd.cgid; - - for (int i = WDCG_START_ID; i <= WDCG_END_ID; i++) { - if (cgutil_vaddr_back[i]->used == 0 || cgutil_vaddr_back[i]->ginfo.wd.cgid != cls || - cgutil_vaddr_back[i]->ginfo.wd.wdlevel == 1) - continue; - - /* update the workload group */ - if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[i])) - return -1; - } - } - - return 0; -} - -/* - * @Description: recover groups by creating new cgroups - * @IN cls_add : class id - * @IN wd_add : array of all new workload groups - * @Return: - * -1: abnormal 0: normal - * @See also: - */ -int cgexec_recover_create_groups(int cls_add, const int* wd_add) -{ - int wld = 0, level = 0, i = 0, tmpcls; - int tmpwld[MAX_WD_LEVEL] = {0}; - errno_t sret; - - sret = memset_s(tmpwld, sizeof(tmpwld), 0, sizeof(tmpwld)); - securec_check_errno(sret, , -1); - - /* get the class info */ - wld = wd_add[0]; - tmpcls = cgutil_vaddr_back[wld]->ginfo.wd.cgid; - - /* verify the information */ - if (cls_add && cls_add != tmpcls) { - fprintf(stderr, "ERROR: new workload group doesn't match class group!\n"); - return -1; - } - - if (cls_add == 0 && wd_add[1]) { - fprintf(stderr, - "ERROR: find more than one added workload group " - "when only workload group is recovering.\n"); - return -1; - } - - /* search workload group */ - for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { - if (cgutil_vaddr_back[i]->used && cgutil_vaddr_back[i]->ginfo.wd.cgid == tmpcls) { - level = cgutil_vaddr_back[i]->ginfo.wd.wdlevel; - tmpwld[level - 1] = i; - } - } - - /* delete class group firstly if only workload group should be added */ - if (cls_add == 0 && -1 == cgexec_delete_default_cgroup(cgutil_vaddr[tmpcls])) - return -1; - - /* copy class group info */ - sret = memcpy_s(cgutil_vaddr[tmpcls], sizeof(gscgroup_grp_t), cgutil_vaddr_back[tmpcls], sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , -1); - - /* reset the values */ - cgutil_vaddr[tmpcls]->ginfo.cls.maxlevel = 0; - cgutil_vaddr[tmpcls]->ginfo.cls.rempct = 100; - cgconf_update_class_percent(); - - /* create class group */ - if (-1 == cgexec_create_new_cgroup(cgutil_vaddr[tmpcls])) { - cgconf_reset_class_group(tmpcls); - return -1; - } - - /* create workload group in a loop */ - for (i = 0; i < MAX_WD_LEVEL; i++) { - wld = tmpwld[i]; - if (wld == 0) - break; - - /* copy workload group info */ - sret = memcpy_s(cgutil_vaddr[wld], sizeof(gscgroup_grp_t), cgutil_vaddr_back[wld], sizeof(gscgroup_grp_t)); - securec_check_errno(sret, , -1); - - if (i) /* level > 1, is not TopWD */ - cgutil_vaddr[tmpcls]->ginfo.cls.rempct -= cgutil_vaddr[wld]->ginfo.wd.percent; - - /* create the workload cgroup */ - if (-1 == cgexec_create_workload_cgroup(cgutil_vaddr[wld])) { - cgconf_reset_workload_group(wld); - return -1; - } - } - - /* create timeshare cgroup */ - if (-1 == cgexec_create_timeshare_cgroup(cgutil_vaddr[tmpcls])) - return -1; - - return 0; -} - -/* - * @Description: recover the last group when failure happened - * @Return: - * -1: abnormal 0: normal - * @See also: - */ -int cgexec_recover_groups(void) -{ - void* vaddr = NULL; - size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); - int cls_add = 0, cls_del = 0, wd_del = 0; - int clspct_update = 0, wdpct_update = 0, quota_update = 0, other_update = 0; - int wd_add[MAX_WD_LEVEL] = {0}; - int j = 0; - errno_t sret; - - /* reset the array */ - sret = memset_s(wd_add, sizeof(wd_add), 0, sizeof(wd_add)); - securec_check_errno(sret, , -1); - - /* get the mapping address of backup file */ - vaddr = cgconf_map_backup_conffile(false); - if (NULL == vaddr) - return -1; - - for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { - cgutil_vaddr_back[i] = (gscgroup_grp_t*)vaddr + i; - - /* find the different memory region of group entry */ - if ((i >= CLASSCG_START_ID && i <= WDCG_END_ID) && - (0 != memcmp(cgutil_vaddr[i], cgutil_vaddr_back[i], sizeof(gscgroup_grp_t)))) { - if (i >= CLASSCG_START_ID && i <= CLASSCG_END_ID) { - if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used == 0) - cls_del = i; - else if (cgutil_vaddr[i]->used == 0 && cgutil_vaddr_back[i]->used) - cls_add = i; - else if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used && - cgexec_check_update_groups(cgutil_vaddr[i], cgutil_vaddr_back[i])) { - /* dynamic update */ - if (cgutil_vaddr[i]->ginfo.cls.percent != cgutil_vaddr_back[i]->ginfo.cls.percent) - clspct_update = i; - else if (cgutil_vaddr[i]->ainfo.quota != cgutil_vaddr_back[i]->ainfo.quota) - quota_update = i; - else - other_update = i; - } - } else if (i > WDCG_START_ID) { - if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used == 0) - wd_del = i; - else if (cgutil_vaddr[i]->used == 0 && cgutil_vaddr_back[i]->used) { - if (j == MAX_WD_LEVEL) { - fprintf(stderr, "ERROR: configure file has more than %d different workload!\n", MAX_WD_LEVEL); - goto error; - } - wd_add[j++] = i; - } else if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used && - cgexec_check_update_groups(cgutil_vaddr[i], cgutil_vaddr_back[i])) { - /* dynamic update */ - if (cgutil_vaddr[i]->ginfo.wd.percent != cgutil_vaddr_back[i]->ginfo.wd.percent) - wdpct_update = i; - else if (cgutil_vaddr[i]->ainfo.quota != cgutil_vaddr_back[i]->ainfo.quota) { - if (quota_update) { - fprintf(stderr, - "ERROR: cpu core quota has been set on %d, " - "it should not be appear on %d again!\n", - quota_update, - i); - goto error; - } - quota_update = i; - } else - other_update = i; - } - } - } - } - - /* -u class update */ - if (clspct_update && -1 == cgexec_recover_update_percent_groups(clspct_update)) - goto error; - - /* -u workload update */ - if (wdpct_update && -1 == cgexec_recover_update_percent_groups(wdpct_update)) - goto error; - - /* -u --fixed update */ - if (quota_update && -1 == cgexec_recover_update_quota_groups(quota_update)) - goto error; - - /* like blkio throttle update */ - if (clspct_update == 0 && wdpct_update == 0 && quota_update == 0 && other_update && - (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[other_update]))) - goto error; - - /* delete the class group directly */ - if (cls_del) { - if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls_del])) - goto error; - cgconf_reset_class_group(cls_del); - } else if (cls_del == 0 && wd_del) /* delete the workload group */ - { - (void)cgexec_delete_workload_cgroup(cgutil_vaddr[wd_del]); - } - - /* add class and workload group */ - if (wd_add[0]) { - if (-1 == cgexec_recover_create_groups(cls_add, wd_add)) - goto error; - } - - /* recover the configuration file finally */ - sret = memcpy_s(cgutil_vaddr[0], cglen, vaddr, cglen); - securec_check_errno(sret, (void)munmap(vaddr, cglen); cgconf_remove_backup_conffile();, -1); - - (void)munmap(vaddr, cglen); - cgconf_remove_backup_conffile(); - return 0; - -error: - securec_check_errno(sret, (void)munmap(vaddr, cglen);, -1); - cgconf_remove_backup_conffile(); - return -1; -} - -/* - * @Description: mount control groups. - * @IN : void - * @Return: void - * @See also: - */ -void cgexec_mount_cgroups(void) -{ - /* mount the cgroup */ - (void)cgexec_mount_root_cgroup(); -} - -/* - * @Description: unmount control groups. - * @IN : void - * @Return: void - * @See also: - */ -void cgexec_umount_cgroups(void) -{ - /* umount the cgroup */ - (void)cgexec_umount_root_cgroup(); -} - -/* - * function name: cgexec_create_cm_default_cgroup - * description : create cm default cgroup - * arguments : void - * return value : - * -1: abnormal - * 0: normal - * Note: the function is used when creating new Cgroup. - */ - -int cgexec_create_cm_default_cgroup(void) -{ - int ret = 0; - errno_t rc = EOK; - char cgpath[GPNAME_PATH_LEN] = {0}; - struct stat buf; - - rc = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); - securec_check_errno(rc, , -1); - rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s/%s:%s", cgutil_opt.mpoints[0], GSCGROUP_CM, cgutil_opt.user); - securec_check_ss_c(rc, "\0", "\0"); - - // creating cm cgroup must be run by root user. - if (geteuid() != 0) - return 0; - - if (0 == stat(cgpath, &buf)) { - fprintf(stderr, "'%s' exists, omit to create this cgroup.\n", cgpath); - return 0; - } - - rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s:%s", GSCGROUP_CM, cgutil_opt.user); - securec_check_ss_c(rc, "\0", "\0"); - - ret = cgexec_create_default_cgroup(cgpath, DEFAULT_CM_CPUSHARES, DEFAULT_IO_WEIGHT, cgutil_allset); - if (ret == -1) { - fprintf(stderr, "can not create cm cgroup, cgpath is %s.\n", cgpath); - return -1; - } - - return 0; -} - -/* delete cm cgroup */ -int cgexec_delete_cm_cgroup(void) -{ - int ret = 0; - errno_t rc = EOK; - char cgpath[GPNAME_PATH_LEN] = {0}; - struct stat buf; - - rc = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); - securec_check_errno(rc, , -1); - rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s/%s:%s", cgutil_opt.mpoints[0], GSCGROUP_CM, cgutil_opt.user); - securec_check_ss_c(rc, "\0", "\0"); - - if (0 != stat(cgpath, &buf)) { - return -1; - } - - rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s:%s", GSCGROUP_CM, cgutil_opt.user); - securec_check_ss_c(rc, "\0", "\0"); - - ret = cgexec_delete_cgroups(cgpath); - if (ret == -1) { - fprintf(stderr, "can not create cm cgroup,cgpath is %s.\n", cgpath); - return -1; - } - - return 0; -} -- 2.34.1 From 0c63707f5e9b075f1deb205d76fca052e279bbde Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:01:55 +0800 Subject: [PATCH 06/56] ADD file via upload --- src/bin/gs_cgroup/cgexec.cpp | 4788 ++++++++++++++++++++++++++++++++++ 1 file changed, 4788 insertions(+) create mode 100644 src/bin/gs_cgroup/cgexec.cpp diff --git a/src/bin/gs_cgroup/cgexec.cpp b/src/bin/gs_cgroup/cgexec.cpp new file mode 100644 index 000000000..a50dd11e5 --- /dev/null +++ b/src/bin/gs_cgroup/cgexec.cpp @@ -0,0 +1,4788 @@ +锘/** + * cgexec.cpp + * Cgroup閰嶇疆鏂囦欢澶勭悊鍑芥暟 + * + * IDENTIFICATION + * src/bin/gs_cgroup/cgconf.cpp + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "workload/gscgroup.h" + +#include "cgutil.h" +#include "securec.h" +#include "bin/elog.h" + +#ifdef ENABLE_UT +#define static +#endif + +#define MAX_COMMAND_LENGTH 128 +#define MOUNT_POINT_LENGTH (MAXPGPATH + 16) + +int cgutil_is_sles11_sp2 = 0; /* 鐢ㄤ簬鎸囩ず褰撳墠鎿嶄綔绯荤粺鏄惁涓篠LES SP2鐗堟湰 */ + +char* cgutil_subsys_table[] = { + MOUNT_CPU_NAME, MOUNT_CPUACCT_NAME, MOUNT_BLKIO_NAME, MOUNT_CPUSET_NAME, MOUNT_MEMORY_NAME }; + +static gscgroup_grp_t* cgutil_vaddr_back[GSCGROUP_ALLNUM] = { NULL }; /* 鐢ㄤ簬鎭㈠ */ + +/* + ***************** STATIC FUNCTIONS ************************ + */ + /* + * 鐢ㄤ簬鏇存柊涓嶅悓灞傛鐨勭粍鐨刢puset鐨勯潤鎬佸嚱鏁帮紝 + * 鍦ㄦ澹版槑浠ヤ究鍑芥暟璋冪敤璇ュ嚱鏁伴噸缃笉鍚岀骇鍒粍鐨凜PU鏍稿績銆 + */ +static int cgexec_update_cgroup_cpuset_value(char* relpath, char* cpuset); +/* 鏇存柊绫诲埆鐨凜PU鏍稿績鍜屾墍灞炵殑宸ヤ綔璐熻浇缁勩*/ +static int cgexec_update_class_cpuset(int cls, char* cpuset); +/* 鏇存柊椤剁骇缁勭殑CPU鏍稿績鍜屽叾鎵鏈夐毝灞炵粍銆*/ +static int cgexec_update_top_group_cpuset(int top, char* cpuset); +/* 鏇存柊涓涓粍鐨凜PU鏍稿績銆 */ +static int cgexec_update_cgroup_cpuset(gscgroup_grp_t* grp, char* cpuset); + +int CheckBackendEnv(const char* input_env_value) +{ + const int max_env_len = 1024; + const char* danger_character_list[] = { ";", "`", "\\", "'", "\"", ">", "<", "$", "&", "|", "!", "\n", NULL }; + int i = 0; + + if (input_env_value == nullptr || strlen(input_env_value) >= max_env_len) { + fprintf(stderr, "ERROR: 閿欒鐨勭幆澧冨彉閲 \"%s\"\n", input_env_value); + return -1; + } + + for (i = 0; danger_character_list[i] != NULL; i++) { + if (strstr((const char*)input_env_value, danger_character_list[i])) { + fprintf(stderr, "ERROR: 鐜鍙橀噺 \"%s\" 鍖呭惈闈炴硶瀛楃 \"%s\".\n", + input_env_value, danger_character_list[i]); + return -1; + } + } + return 0; +} + +inline int CheckSystemSucess(pid_t status) +{ + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + return 0; + } + else { + fprintf(stderr, "鍛戒护鎵ц澶辫触: %d!\n", WEXITSTATUS(status)); + return -1; + } +} + +/* + * 鍑芥暟鍚嶇О: cgexec_get_cgroup_number + * 鎻忚堪锛氳幏鍙朇group鏁伴噺 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 鍏朵粬鍊: 姝e父 + */ +static int cgexec_get_cgroup_number(void) +{ + char buf[PROCLINE_LEN]; + FILE* f = NULL; + char* p = NULL, * q = NULL; + int hierarchy; + int cgcnt = -1; + + f = fopen("/proc/cgroups", "r"); + if (f == NULL) + return -1; + + while (NULL != fgets(buf, PROCLINE_LEN, f)) { + /* 渚嬪瓙鏉ヨ嚜/proc锛 + * #subsys_name hierarchy num_cgroups enabled + * cpu 0 1 1 + * + * 鑾峰彇绗竴鏍忥紝渚嬪cpu + */ + p = buf; + q = strchr(p, '\t'); + if (q == NULL) + continue; + + *q = '\0'; + if (0 == strcmp(MOUNT_CPU_NAME, p)) { + while (*(q++) == ' ') + continue; + + /* 鑾峰彇绗簩鏍 */ + p = strchr(q, '\t'); + if (p == NULL) + break; + + *p = '\0'; + + hierarchy = (int)strtol(q, NULL, 10); + if (hierarchy == 0) { + fprintf(stderr, "cgroup鏈寕杞斤紒\n"); + break; + } + + while (*(p++) == ' ') + continue; + + /* 鑾峰彇绗笁鏍 */ + q = strchr(p, '\t'); + if (q == NULL) { + fclose(f); + return -1; + } + *q = '\0'; + + cgcnt = (int)strtol(p, NULL, 10); + } + } + + fclose(f); + return cgcnt; +} +/* + * @Description: 妫鏌puset鐨勫笺 + * @IN clsset: class cpuset锛堢被cpuset锛 + * @IN grpset: group cpuset锛堢粍cpuset锛 + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ +int cgexec_check_cpuset_value(const char* clsset, const char* grpset) +{ + int clsstart, clsend; // class cpuset鐨勮捣濮嬪煎拰缁撴潫鍊 + int grpstart, grpend; // group cpuset鐨勮捣濮嬪煎拰缁撴潫鍊 + + errno_t ret = sscanf_s(clsset, "%d-%d", &clsstart, &clsend); // 浠巆lsset瑙f瀽鍑篶puset鐨勮捣濮嬪煎拰缁撴潫鍊 + if (ret != 2) { // 瑙f瀽澶辫触 + fprintf(stderr, + "%s:%d failed on calling " + "security function.\n", + __FILE__, + __LINE__); + return -1; + } + ret = sscanf_s(grpset, "%d-%d", &grpstart, &grpend); // 浠巊rpset瑙f瀽鍑篶puset鐨勮捣濮嬪煎拰缁撴潫鍊 + if (ret != 2) { // 瑙f瀽澶辫触 + fprintf(stderr, + "%s:%d failed on calling " + "security function.\n", + __FILE__, + __LINE__); + return -1; + } + + /* group cpuset鐨勫煎繀椤诲湪class cpuset鑼冨洿鍐 */ + if (grpstart >= clsstart && grpend <= clsend) + return 0; + + return -1; +} + +/* + * @Description: 鑾峰彇cpuset鐨勯暱搴︺ + * @IN cpuset: 寰呰В鏋愮殑cpuset + * @OUT start: cpuset鐨勮捣濮嬪 + * @OUT end: cpuset鐨勭粨鏉熷 + * @Return: cpuset鐨勯暱搴 + * @See also: + */ +static int cgexec_get_cpuset_length(const char* cpuset, int* start, int* end) +{ + errno_t ret = sscanf_s(cpuset, "%d-%d", start, end); // 浠巆puset瑙f瀽鍑鸿捣濮嬪煎拰缁撴潫鍊 + if (ret != 2) { // 瑙f瀽澶辫触 + fprintf(stderr, + "%s:%d failed on calling " + "security function.\n", + __FILE__, + __LINE__); + return -1; + } + + return *end - *start + 1; // 杩斿洖cpuset鐨勯暱搴 +} + +/* + * @Description: 灏嗚捣濮嬪煎拰缁撴潫鍊煎鍒跺埌cpuset涓 + * @OUT cpuset: 璁剧疆濂界殑cpuset + * @IN start: cpuset鐨勮捣濮嬪 + * @IN end: cpuset鐨勭粨鏉熷 + * @See also: + */ +static void cgexec_get_cpu_core_range(char* cpuset, int start, int end) +{ + errno_t ret = sprintf_s(cpuset, CPUSET_LEN, "%d-%d", start, end); // 灏嗚捣濮嬪煎拰缁撴潫鍊兼牸寮忓寲鎴愬瓧绗︿覆骞跺鍒跺埌cpuset涓 + securec_check_intval(ret, , ); // 妫鏌printf_s鐨勮繑鍥炲 +} + +/* + * @Description : 灏嗙櫨鍒嗘瘮杞崲涓篶puset鐨勯暱搴︺ + * @IN whole : 涓婁竴绾х粍鐨刢puset闀垮害 + * @IN wdpct : 鐢ㄦ埛璁剧疆鐨勭櫨鍒嗘瘮鍊("--fixed") + * @Return : -1: 寮傚父 + * @Return : cpusetlength: 闇瑕佹洿鏂扮殑cpuset鐨勯暱搴 + * @See also: + */ +static int cgexec_trans_percent_to_cpusets(int whole, int wdpct) +{ + int cpusetlength = 0; + char tempvalue[CPUSET_LEN] = { 0 }; // 涓存椂瀛樺偍杞崲鍚庣殑鐧惧垎姣斿煎瓧绗︿覆 + char* temp = NULL; + errno_t ret; + + ret = sprintf_s(tempvalue, CPUSET_LEN, "%.1f", (float)whole * wdpct / GROUP_ALL_PERCENT); // 璁$畻鐧惧垎姣斿煎苟鏍煎紡鍖栨垚瀛楃涓 + securec_check_intval(ret, , -1); // 妫鏌printf_s鐨勮繑鍥炲 + + temp = strchr(tempvalue, '.'); // 鏌ユ壘灏忔暟鐐 + cpusetlength = atoi(tempvalue); // 灏嗗瓧绗︿覆杞崲涓烘暣鏁 + + if ((temp != NULL) && (*(++temp) > '5' || cpusetlength == 0)) { + cpusetlength++; // 濡傛灉灏忔暟鐐瑰悗鐨勬暟瀛楀ぇ浜5鎴朿pusetlength涓0锛屽垯鍚戜笂鍙栨暣 + } + + return cpusetlength; // 杩斿洖cpuset鐨勯暱搴 +} +/** + * 鍔熻兘锛氬皢cpuset鐨勯暱搴﹁浆鎹负鐧惧垎姣 + * 鍙傛暟锛 + * - highlen锛氫笂灞傜粍鐨刢puset闀垮害 + * - lowlen锛氱敤鎴疯缃殑鐧惧垎姣斿硷紙"--fixed"锛 + * 杩斿洖鍊硷細 + * - cpusetlength锛氳鏇存柊鐨刢puset鐨勯暱搴 + * 鍙﹁鍙傞槄锛 + */ +static int cgexec_trans_cpusets_to_percent(int highlen, int lowlen) { + int pct = 0; + + // 濡傛灉涓婂眰缁勭殑cpuset闀垮害涓0锛屽垯杈撳嚭閿欒淇℃伅骞惰繑鍥-1 + if (highlen == 0) { + fprintf(stderr, "ERROR: %s:%d, Division by zero!\n", __FILE__, __LINE__); + return -1; + } + + // 璁$畻鐧惧垎姣斿 + pct = lowlen * GROUP_ALL_PERCENT / highlen; + + /* + * 濡傛灉绯荤粺鐨勬牳蹇冩暟灏忎簬100锛屽垯浼樺厛閫夋嫨杈冨皬鐨勭櫨鍒嗘瘮锛 + * 浠ヤ究灏藉彲鑳藉皢cpuset杞崲涓洪厤棰濄 + * + * 纭繚浠庣櫨鍒嗘瘮鑾峰緱鐨勯暱搴︿笌浣庨暱搴︾浉鍚屻 + */ + while (cgexec_trans_percent_to_cpusets(highlen, pct) < lowlen || !pct) + pct++; + + return pct; +} + +/** + * 鍔熻兘锛氳幏鍙朿group id鑼冨洿 + * 鍙傛暟锛 + * - high锛氱粍id + * - forstart锛氳寖鍥寸殑璧峰缁刬d锛堣緭鍑哄弬鏁帮級 + * - forend锛氳寖鍥寸殑缁撴潫缁刬d锛堣緭鍑哄弬鏁帮級 + * 杩斿洖鍊硷細 + * - -1锛氬紓甯 + * - 0锛歝puset宸茶缃ソ + * 鍙﹁鍙傞槄锛 + */ +int cgexec_get_cgroup_id_range(int high, int* forstart, int* forend) { + if (high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) { + *forstart = WDCG_START_ID; + *forend = WDCG_END_ID; + } + else if (high == TOPCG_CLASS) { + *forstart = CLASSCG_START_ID; + *forend = CLASSCG_END_ID; + } + else if (high == TOPCG_BACKEND) { + *forstart = BACKENDCG_START_ID; + *forend = BACKENDCG_END_ID; + } + else if (high == TOPCG_GAUSSDB) { + *forstart = TOPCG_BACKEND; + *forend = TOPCG_CLASS; + } + else + return -1; + + return 0; +} +/** + * 鍔熻兘锛氭鏌ヤ綆绾х粍鐨勬荤櫨鍒嗘瘮鏄惁瓒呰繃涓婇檺 + * 鍙傛暟锛 + * - high锛氫綆绾х粍鎵灞炵殑楂樼骇缁勭殑ID + * - low锛氳鏇存柊鐨勪綆绾х粍鐨処D + * - cpuset锛氬鏋滄垚鍔燂紝璁$畻寰楀埌鐨刢puset灏嗗瓨鍌ㄥ湪鍏朵腑 + * 杩斿洖鍊硷細 + * - -1锛氬紓甯 + * - 0锛歝puset宸茶缃ソ + * - 1锛氶渶瑕侀噸鏂拌缃 + * 鍙﹁鍙傞槄锛 + */ +static int cgexec_check_cpuset_percent(int high, int low, char* cpuset) { + /* 寰幆鐨勮捣濮嬪煎拰缁撴潫鍊 */ + int forstart = 0, forend = 0; + /* 浣庣骇缁勫拰楂樼骇缁勭殑cpuset鐨勮捣濮嬪煎拰缁撴潫鍊 */ + int i, highstart = 0, highend = 0, lowstart = 0, lowend = 0; + /* 蹇界暐鎺夎鏇存柊鐨勪綆绾х粍鍚庣殑cpu鏍稿績鏁板拰閰嶉鎬诲拰 */ + int sum_cpusets = 0, sum_quota = 0; + /* 楂樼骇缁勫拰褰撳墠浣庣骇缁勭殑cpuset闀垮害浠ュ強褰撳墠浣庣骇缁勭殑鏈澶у */ + int lowlen = 0, highlen = 0, lowmax = 0; + /* 鐢ㄤ簬淇濆瓨杩斿洖鍊肩殑cpuset鐨勮捣濮嬪煎拰缁撴潫鍊 */ + int ret_start, ret_end; + + if (cgexec_get_cgroup_id_range(high, &forstart, &forend) == -1) + return -1; + + /* 鑾峰彇楂樼骇缁勭殑cpuset闀垮害 */ + highlen = cgexec_get_cpuset_length(cgutil_vaddr[high]->cpuset, &highstart, &highend); + + /* 璁$畻瑕佹洿鏂扮殑cpuset鐨勯暱搴 */ + lowlen = cgexec_trans_percent_to_cpusets(highlen, cgutil_opt.setspct); + + for (i = forstart; i <= forend; i++) { + /* 褰撳墠澶勭悊鐨勪綆绾х粍琚拷鐣 */ + if (cgutil_vaddr[i]->used == 0 || i == low) + continue; + + /* 鍙冭檻鍏锋湁鐩稿悓绫诲埆鈥渉igh鈥濈殑宸ヤ綔璐熻浇缁 */ + if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && high != cgutil_vaddr[i]->ginfo.wd.cgid) + continue; + + /* + * 涓轰簡妫鏌ユ柊璁剧疆鐨剆etspct鏄惁浣垮緱鎬荤殑cpu鏍稿績鏁拌秴鍑鸿寖鍥达紝 + * 璁$畻缇ょ粍鐨刢pu鏍稿績鏁般佹渶澶ф牳蹇冨煎拰鎬荤殑閰嶉鍊硷紝蹇界暐瑕佹洿鏂扮殑浣庣骇缁勩 + * + * 閰嶉鏄痗pu鏍稿績鏁扮殑鐧惧垎姣旓紝 + * 濡傛灉閰嶉涓0锛屽垯cpu鏍稿績鏁板皢浣跨敤榛樿鍊笺 + */ + if (cgutil_vaddr[i]->ainfo.quota) { + sum_cpusets += cgexec_get_cpuset_length(cgutil_vaddr[i]->cpuset, &lowstart, &lowend); + sum_quota += cgutil_vaddr[i]->ainfo.quota; + lowmax = (lowend > lowmax) ? lowend : lowmax; + } + } + + /* + * 濡傛灉閰嶉鍊煎拰鏂拌缃殑setspct瓒呭嚭鑼冨洿锛 + * 鍒欐姏鍑洪敊璇傜劧鑰岋紝鏈変簺鎯呭喌涓嬶紝閰嶉涓嶈秴鍑鸿寖鍥达紝浣嗘槸cpu鏍稿績鏁拌秴鍑鸿寖鍥达紝 + * 鍥犱负璁$畻寰楀埌鐨勫皬鏁帮紙渚嬪锛1.6浼氬洓鑸嶄簲鍏ヤ负2锛0.1浼氬洓鑸嶄簲鍏ヤ负1锛1.5浼氬洓鑸嶄簲鍏ヤ负1锛夊洓鑸嶄簲鍏ャ + * 杩欎簺鎯呭喌灏嗗湪瀹廏ET_CPUSET_START_VALUE涓鐞嗐 + * 渚嬪锛屽鏋滄柊璁剧疆鐨勭粍杩樻湁2涓牳蹇冨墿浣欙紝浣嗘槸缁忚繃setspct璁$畻闇瑕3涓牳蹇冿紝 + * 鍒欏皢璁剧疆鏈鍚庝笁涓牳蹇冦 + */ + if (sum_quota + cgutil_opt.setspct > GROUP_ALL_PERCENT) { + if (*cgutil_vaddr[low]->grpname) + fprintf(stderr, + "ERROR: cpu鏍稿績鐨勬荤櫨鍒嗘瘮澶т簬100锛屾棤娉曚负缁刓"%s\"璁剧疆%d%%\n", + cgutil_vaddr[low]->grpname, + cgutil_opt.setspct); + + return -1; + } + + ret_start = GET_CPUSET_START_VALUE(highstart, highend, sum_cpusets, lowmax, lowlen); + ret_end = ret_start + lowlen - 1; + cgexec_get_cpu_core_range(cpuset, ret_start, ret_end); + + /* 杩斿洖鍊艰〃绀烘槸鍚﹂渶瑕侀噸鏂拌缃 */ + return (ret_start > lowmax) ? 0 : 1; +} +/* + * @Description : 閲嶇疆缁勭殑cpuset鍊笺 + * @IN high : 浣庣骇缁勬墍灞炵殑楂樼骇缁刬d + * @IN low : 瑕佹洿鏂扮殑浣庣骇缁勶紝涓嶅寘鎷湪閲嶇疆鍒楄〃涓 + * @Return -1 : 寮傚父 + * @Return 0 : 姝e父銆 + * @See also: + */ +static int cgexec_reset_cpuset_cgroups(int high, int low) +{ + int forstart = 0, forend = 0; // 寰幆鐨勫紑濮嬪煎拰缁撴潫鍊 + int i = 0; + int lowlen = 0, highlen = 0; // 浣庣骇鍜岄珮绾puset鐨勯暱搴 + int lowstart = 0, lowend = 0; // 浣庣骇缁刢puset鐨勮捣濮嬪煎拰缁撴潫鍊 + int highstart = 0, highend = 0; // 楂樼骇缁刢puset鐨勮捣濮嬪煎拰缁撴潫鍊 + char sets[CPUSET_LEN]; // 瑕佹洿鏂扮殑璁$畻濂界殑cpuset + bool flag = false; // 琛ㄧず绗竴娆¤繘鍏ュ惊鐜殑鏍囧織 + + if (cgexec_get_cgroup_id_range(high, &forstart, &forend) == -1) + return -1; + + // 鑾峰彇楂樼骇缁勭殑cpuset闀垮害 + highlen = cgexec_get_cpuset_length(cgutil_vaddr[high]->cpuset, &highstart, &highend); + + for (i = forstart; i <= forend; i++) { + // 鍦ㄩ噸缃垪琛ㄤ腑蹇界暐浣庣骇缁 + if (cgutil_vaddr[i]->used == 0 || (low != 0 && i == low)) + continue; + + // 鍙冭檻灞炰簬楂樼骇绫诲埆鐨勫伐浣滆礋杞界粍 + if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && high != cgutil_vaddr[i]->ginfo.wd.cgid) + continue; + + // 鍙冭檻鍏锋湁閰嶉鍊肩殑缁 + if (cgutil_vaddr[i]->ainfo.quota) { + // 浣庣骇缁勶紙涓"low"鐩稿悓绾у埆鐨勭粍锛夌殑cpu鏍稿績闀垮害 + lowlen = cgexec_trans_percent_to_cpusets(highlen, cgutil_vaddr[i]->ainfo.quota); + + /* + * 鍙湁绗竴娆¤繘鍏ュ惊鐜椂锛宖lag涓篺alse + * cpu鏍稿績浠庨珮绾х粍鐨刢pu鏍稿績鑼冨洿鎸夐『搴忓垎閰嶃 + * 瑕侀噸缃殑绗竴缁勪粠"highstart"寮濮嬪垎閰嶏紝 + * 涓嬩竴缁勪粠鍓嶄竴缁勭殑"lowend" + 1寮濮嬪垎閰 + */ + lowstart = flag ? (lowend + 1) : highstart; + lowend = lowstart + lowlen - 1; + + /* + * 璋冪敤姝ゅ嚱鏁扮殑璋冪敤鑰呭彲浠ヤ繚璇佹婚厤棰濅笉瓒呭嚭鑼冨洿锛 + * 鎵浠ユ垜浠彧闇瑕佹鏌ュ墿浣欑殑cpu鏍稿績鏄惁瓒冲锛 + * 涓嶈冻鐨勬儏鍐靛皢涓巆gexec_check_cpuset_percent澶勭悊鏂瑰紡鐩稿悓銆 + */ + if (lowend > highend) { + lowstart = highend - lowlen + 1; + lowend = highend; + } + // "sets"淇濆瓨瑕侀噸缃殑cpuset + cgexec_get_cpu_core_range(sets, lowstart, lowend); + + // 浣跨敤"sets"閲嶇疆缁勭殑cpuset + if ((high == TOPCG_CLASS && cgexec_update_class_cpuset(i, sets) == -1) || + (high == TOPCG_GAUSSDB && cgexec_update_top_group_cpuset(i, sets) == -1) || + (((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) || high == TOPCG_BACKEND) && + (cgexec_update_cgroup_cpuset(cgutil_vaddr[i], sets) == -1))) { + fprintf(stderr, "ERROR: reset cpu cores for \"%s\" failed\n", cgutil_vaddr[i]->grpname); + return -1; + } + + // 涓嬩竴娆¤繘鍏ュ惊鐜椂锛宖lag灏嗕负true + if (!flag) + flag = true; + } + /* + * 瀵逛簬閲嶇疆鍚庣缁勬垨宸ヤ綔璐熻浇缁勭殑鎯呭喌锛岀敱浜庡畠浠病鏈 + * 浣庣骇缁勶紝鍥犳涓嶉渶瑕侀噸缃 + * 鍦ㄥ叾浠栨儏鍐典笅锛岄渶瑕侀掑綊鍦伴噸缃綆绾х粍銆 + */ + if ((high == TOPCG_CLASS || high == TOPCG_GAUSSDB) && cgexec_reset_cpuset_cgroups(i, 0) == -1) { + fprintf( + stderr, "ERROR: reset group failed when reseting cpuset for group \"%s\".\n", cgutil_vaddr[i]->grpname); + return -1; + } + } + return 0; +} +/* + * @Description : 鑾峰彇鍏锋湁鈥渉igh鈥濅綔涓洪珮绾х粍ID鐨勭粍鐨勬籆PU鏍稿績鐧惧垎姣 + * @IN high : 楂樼骇缁処D + * @Return : 鎬婚厤棰濆 + * @See also: + */ +static int cgexec_check_fixed_percent(int high) +{ + int forstart = 0, forend = 0; // 寰幆鐨勮捣濮嬪拰缁撴潫鍊 + int sets_total_pct = 0; // 浣庣骇缁勭殑鎬荤櫨鍒嗘瘮 + int i = 0; + + (void)cgexec_get_cgroup_id_range(high, &forstart, &forend); // 鑾峰彇璧峰鍜岀粨鏉熷 + + for (i = forstart; i <= forend; i++) { + if (cgutil_vaddr[i]->used == 0 || !cgutil_vaddr[i]->ainfo.quota) + continue; + + /* 浠呰冭檻灞炰簬楂樼骇绫诲埆鐨勫伐浣滆礋杞界粍 */ + if ((high >= CLASSCG_START_ID && high <= CLASSCG_END_ID) && high != cgutil_vaddr[i]->ginfo.wd.cgid) + continue; + + sets_total_pct += cgutil_vaddr[i]->ainfo.quota; + } + + return sets_total_pct; +} + +/* + * @Description: 鑾峰彇杈冨ぇ鐨刢puset鍊笺 + * @IN clsset: 绫诲埆cpuset + * @IN grpset: 缁刢puset + * @OUT result: 杈冨ぇ鐨刢puset + * @Return: 杈冨ぇ鐨刢puset鍊 + * @See also: + */ +char* cgexec_get_large_cupset(const char* clsset, const char* grpset, char* result) +{ + int clsstart, clsend; + int grpstart, grpend; + int resstart, resend; + + int rc = sscanf_s(clsset, "%d-%d", &clsstart, &clsend); + if (rc != 2) { // 妫鏌scanf_s鍑芥暟鐨勮繑鍥炲 + fprintf(stderr, + "%s:%d 鍦ㄨ皟鐢ㄥ畨鍏ㄥ嚱鏁板け璐ャ俓n", + __FILE__, + __LINE__); + return NULL; + } + + rc = sscanf_s(grpset, "%d-%d", &grpstart, &grpend); + if (rc != 2) { // 妫鏌scanf_s鍑芥暟鐨勮繑鍥炲 + fprintf(stderr, + "%s:%d 鍦ㄨ皟鐢ㄥ畨鍏ㄥ嚱鏁板け璐ャ俓n", + __FILE__, + __LINE__); + return NULL; + } + + /* 鑾峰彇杈冨ぇ鐨勮捣濮嬪 */ + resstart = (clsstart < grpstart) ? clsstart : grpstart; + /* 鑾峰彇杈冨ぇ鐨勭粨鏉熷 */ + resend = (clsend > grpend) ? clsend : grpend; + + /* 鑾峰彇杈冨ぇ鐨刢puset */ + rc = sprintf_s(result, CPUSET_LEN, "%d-%d", resstart, resend); + /* 妫鏌ュ畨鍏ㄥ嚱鏁扮殑杩斿洖鍊 */ + securec_check_ss_c(rc, "\0", "\0"); + + return result; +} + +/* + * @Description: 鑾峰彇甯︽湁relpath鐨刢group淇℃伅銆 + * @IN relpath: cgroup鐨勭浉瀵硅矾寰 + * @Return: cgroup淇℃伅 + * @See also: + */ +struct cgroup* cgexec_get_cgroup(const char* relpath) +{ + struct cgroup* cg = NULL; + int ret; + + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯 */ + cg = cgroup_new_cgroup(relpath); + if (cg == NULL) { + fprintf(stdout, "ERROR: 鏃犳硶涓%s鍒涘缓鏂扮殑cgroup銆俓n", relpath); + return NULL; + } + + /* 浠庡唴鏍歌幏鍙栧叧浜巆group鐨勬墍鏈変俊鎭 */ + ret = cgroup_get_cgroup(cg); + if (ret != 0) { + fprintf(stdout, "ERROR: 鏃犳硶鑾峰彇%s鐨刢group淇℃伅(%d)\n", cgroup_strerror(ret), ret); + cgroup_free(&cg); + return NULL; + } + + return cg; +} +/* + * 鍑芥暟鍚嶇О锛歝gexec_update_remain_value + * 鍔熻兘鎻忚堪锛氭洿鏂癛emain Cgroup鐨勫姩鎬佸 + * 鍙傛暟锛 + * relpath锛歊emain Cgroup鐨勭浉瀵硅矾寰 + * cpushares锛歝pu.shares鐨勫 + * ioweight锛歜lkio.weight鐨勫 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + * 娉ㄦ剰锛氳鍑芥暟浠呮洿鏂癱pu.shares鍜宐lkio.weight鐨勫笺 + */ + //搴旂敤瀹炰緥锛氬亣璁惧湪鎿嶄綔绯荤粺涓湁涓涓悕涓"root"鐨凜group锛屽叾涓寘鍚簡澶氫釜瀛怌group锛屾瘡涓瓙Cgroup浠h〃涓涓繍琛岀殑搴旂敤绋嬪簭銆傞氳繃璋冪敤`cgexec_update_remain_value`鍑芥暟锛屽彲浠ユ洿鏂"Cpu Share"鍜"I/O Weight"鐨勫硷紝浠ユ帶鍒舵瘡涓瓙Cgroup鐨凜PU鍜孖 / O璧勬簮浣跨敤鎯呭喌銆備緥濡傦紝灏"root" Cgroup涓殑涓涓瓙Cgroup鐨"Cpu Share"璁剧疆涓200锛屽苟灏"I/O Weight"璁剧疆涓300锛屽彲浠ユ敼鍙樿瀛怌group鐩稿浜庡叾浠栧瓙Cgroup鐨凜PU鍜孖 / O璧勬簮鏉冮噸銆 + // + //浠g爜瑙i噴锛 + //1. 鍒嗛厤涓涓柊鐨刢group缁撴瀯锛 + //2. 鑾峰彇涓庢cgroup鐩稿叧鐨勬墍鏈変俊鎭紱 + //3. 鑾峰彇cpu鎺у埗鍣紱 + //4. 濡傛灉cpushares涓嶄负0锛屽垯灏哻pu.shares鐨勫艰缃负cpushares锛 + //5. 灏嗘帶鍒跺櫒鏇存柊鍒板唴鏍镐腑锛 + //6. 閲婃斁鎺у埗鍣ㄥ拰cgroup缁撴瀯锛 + //7. 杩斿洖0琛ㄧず姝e父鎵ц銆 +int cgexec_update_remain_value(char* relpath, u_int64_t cpushares, u_int64_t ioweight) +{ + struct cgroup* cg = NULL; + struct cgroup_controller* cgc_cpu = NULL; + int ret; + + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯 */ + cg = cgroup_new_cgroup(relpath); + if (cg == NULL) { + ret = ECGFAIL; + fprintf(stdout, "ERROR: 鏃犳硶涓%s鍒涘缓鏂扮殑cgroup锛%s\n", relpath, cgroup_strerror(ret)); + return -1; + } + + /* 浠庡唴鏍歌幏鍙栧叧浜巆group鐨勬墍鏈変俊鎭 */ + ret = cgroup_get_cgroup(cg); + if (ret != 0) { + fprintf(stdout, "ERROR: 鏃犳硶鑾峰彇%s cgroup鐨勪俊鎭細%s(%d)\n", relpath, cgroup_strerror(ret), ret); + cgroup_free(&cg); + return -1; + } + + /* 鑾峰彇cpu鎺у埗鍣 */ + cgc_cpu = cgroup_get_controller(cg, MOUNT_CPU_NAME); + if (NULL == cgc_cpu) { + fprintf(stderr, "ERROR: 鍦%s涓坊鍔%s鎺у埗鍣ㄥけ璐ワ紒\n", relpath, MOUNT_CPU_NAME); + cgroup_free(&cg); + return -1; + } + + if (cpushares && (0 != (ret = cgroup_set_value_uint64(cgc_cpu, CPU_SHARES, cpushares)))) { + fprintf(stderr, "ERROR: 鏃犳硶灏%s璁剧疆涓%lu锛%s\n", CPU_SHARES, cpushares, cgroup_strerror(ret)); + cgroup_free_controllers(cg); + cgroup_free(&cg); + return -1; + } + + /* 灏嗘帶鍒跺櫒鏇存柊鍒板唴鏍镐腑 */ + if (0 != (ret = cgroup_modify_cgroup(cg))) { + fprintf(stderr, + "ERROR: 淇敼%s鐨刢group鏃讹紝淇敼鍊煎け璐ワ細%s\n", + cgroup_strerror(ret)); + cgroup_free_controllers(cg); + cgroup_free(&cg); + return -1; + } + + cgroup_free_controllers(cg); + cgroup_free(&cg); + + return 0; +} + +/** + * 鍑芥暟鍚嶇О锛歝gexec_update_remain_cgroup + * 鍔熻兘锛氳幏鍙栧墿浣欑兢缁勭殑鍊硷紝骞跺皢鍏舵洿鏂板埌鍐呮牳缇ょ粍涓 + * 鍙傛暟锛 + * grp锛氬伐浣滆礋杞界兢缁勭殑閰嶇疆淇℃伅锛屼笌鍓╀綑缇ょ粍鍏锋湁鐩稿悓绾у埆 + * cls锛氬叿鏈夊伐浣滆礋杞界兢缁勭殑绫荤殑缁処D + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + * 娉ㄦ剰锛氭鍑芥暟鐢ㄤ簬鏇存柊宸ヤ綔璐熻浇缇ょ粍鐨勫笺 + */ + /* + * 鍔熻兘锛 + * 璇ュ嚱鏁扮敤浜庢洿鏂板伐浣滆礋杞界兢缁勭殑鍊笺 + * + * 鍙橀噺锛 + * grp锛氬伐浣滆礋杞界兢缁勭殑閰嶇疆淇℃伅锛屼笌鍓╀綑缇ょ粍鍏锋湁鐩稿悓绾у埆 + * cls锛氬叿鏈夊伐浣滆礋杞界兢缁勭殑绫荤殑缁処D + * + * 绀轰緥锛 + * 鍦ㄦ煇涓湇鍔$▼搴忎腑锛岄渶瑕佹牴鎹伐浣滆礋杞界兢缁勭殑閰嶇疆淇℃伅鏉ユ洿鏂板墿浣欑兢缁勭殑鍊笺傞氳繃璋冪敤璇ュ嚱鏁帮紝鍙互鑾峰彇鍓╀綑缇ょ粍鐨勫硷紝骞跺皢鍏舵洿鏂板埌鍐呮牳缇ょ粍涓備緥濡傦紝褰撳鍔犳垨鍑忓皯宸ヤ綔璐熻浇缇ょ粍鏃讹紝鍙互浣跨敤璇ュ嚱鏁版潵鏇存柊瀵瑰簲鐨勫墿浣欑兢缁勭殑鍊笺 + */ + + /* + * 璁$畻绾у埆澶т簬鎸囧畾宸ヤ綔璐熻浇缇ょ粍鐨勭兢缁勭殑鍓╀綑鐧惧垎姣斻 + * 閬嶅巻寰幆鍙橀噺i浠2寮濮嬭鏁版槸涓轰簡蹇界暐TopWD缇ょ粍锛岄亶鍘嗗惊鐜彉閲廽鐢ㄤ簬瀵绘壘鎸囧畾宸ヤ綔璐熻浇缇ょ粍鐨勭骇鍒 + * 璁$畻鍓╀綑鐧惧垎姣旂殑鏂瑰紡鏄氳繃鍑忓幓姣忎釜绾у埆鐨勫伐浣滆礋杞界兢缁勭殑鐧惧垎姣斻 + */ + + /* + * 鏇存柊绾у埆澶т簬鎸囧畾宸ヤ綔璐熻浇鐨勫伐浣滆礋杞姐 + * 閬嶅巻寰幆鍙橀噺i鐢ㄤ簬閬嶅巻绾у埆澶т簬鎸囧畾宸ヤ綔璐熻浇鐨勫伐浣滆礋杞斤紝閬嶅巻寰幆鍙橀噺j鐢ㄤ簬瀵绘壘宸ヤ綔璐熻浇绾у埆涓巌鐩稿悓鐨勫伐浣滆礋杞姐 + * 鑾峰彇宸ヤ綔璐熻浇缇ょ粍鐨勭埗璺緞锛屽苟璁剧疆鍓╀綑缇ょ粍璺緞浣滀负鍏宠仈璺緞鐨勪竴閮ㄥ垎銆 + * 璁$畻鍓╀綑缇ょ粍鐨勫硷紝鍖呮嫭CPU浠介鍜孖O鏉冮噸锛屽苟璋冪敤cgexec_update_remain_value鍑芥暟鏇存柊鍓╀綑缇ょ粍鐨勫笺 + */ +static int cgexec_update_remain_cgroup(gscgroup_grp_t* grp, int cls) +{ + char* relpath = NULL; + int i, j, ret, rempct = GROUP_ALL_PERCENT; // 瀹氫箟鍙橀噺锛氬叧鑱旇矾寰勩侀亶鍘嗗惊鐜彉閲廼鍜宩銆佽繑鍥炲笺佸墿浣欑櫨鍒嗘瘮 + char rempath[16]; // 瀹氫箟鍙橀噺锛氬墿浣欒矾寰 + u_int64_t cpushares, ioweight; // 瀹氫箟鍙橀噺锛欳PU浠介鍜孖O鏉冮噸 + errno_t sret; + + /* + * 璁$畻绾у埆澶т簬鎸囧畾宸ヤ綔璐熻浇缇ょ粍鐨勭兢缁勭殑鍓╀綑鐧惧垎姣 + * 浠2寮濮嬭鏁版槸涓轰簡蹇界暐TopWD缇ょ粍 + */ + for (i = 2; i < grp->ginfo.wd.wdlevel; i++) { + /* 璁$畻鍓╀綑鐧惧垎姣 */ + for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { + if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cls && + cgutil_vaddr[j]->ginfo.wd.wdlevel == i) + break; + } + + rempct -= cgutil_vaddr[j]->ginfo.wd.percent; + } + + /* 鏇存柊绾у埆澶т簬鎸囧畾宸ヤ綔璐熻浇鐨勫伐浣滆礋杞 */ + for (i = grp->ginfo.wd.wdlevel; i <= cgutil_vaddr[cls]->ginfo.cls.maxlevel; i++) { + for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { + if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cls && + cgutil_vaddr[j]->ginfo.wd.wdlevel == i) + break; + } + + /* 鑾峰彇宸ヤ綔璐熻浇缇ょ粍鐨勭埗璺緞 */ + relpath = gscgroup_get_parent_wdcg_path(j, cgutil_vaddr, current_nodegroup); + if (NULL == relpath) + return -1; + + /* 鑾峰彇鍓╀綑缇ょ粍璺緞 */ + sret = snprintf_s(rempath, + sizeof(rempath), + sizeof(rempath) - 1, + "%s:%d/", + GSCGROUP_REMAIN_WORKLOAD, + cgutil_vaddr[j]->ginfo.wd.wdlevel); + securec_check_intval(sret, free(relpath), -1); + + sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); + securec_check_errno(sret, free(relpath), -1); + + /* 鏇存柊鍓╀綑缇ょ粍 */ + rempct -= cgutil_vaddr[j]->ginfo.wd.percent; + + cpushares = (u_int64_t)MAX_CLASS_CPUSHARES * rempct / GROUP_ALL_PERCENT; + ioweight = (u_int64_t)IO_WEIGHT_CALC(MAX_IO_WEIGHT, rempct); + + ret = cgexec_update_remain_value(relpath, cpushares, ioweight); + if (-1 == ret) { + free(relpath); + relpath = NULL; + return -1; + } + + free(relpath); + relpath = NULL; + } + + return 0; +} + +/* + * function name: cgexec_update_cgroup_value + * 鍔熻兘锛氭牴鎹粍閰嶇疆淇℃伅鐨勫兼洿鏂癈group淇℃伅 + * 鍙傛暟锛 + * grp锛氱粍鐨勯厤缃俊鎭 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + * 娉ㄦ剰锛氳鍑芥暟鍦ㄦ洿鏂板姩鎬佸煎拰鍥哄畾鍊兼椂浣跨敤銆 + */ +//璇ュ嚱鏁扮殑鍔熻兘鏄牴鎹粍閰嶇疆淇℃伅鐨勫兼洿鏂癈group淇℃伅銆傚弬鏁癵rp鏄粍鐨勯厤缃俊鎭傝繑鍥炲 - 1琛ㄧず寮傚父锛岃繑鍥炲0琛ㄧず姝e父銆 +//璇ュ嚱鏁伴鍏堣幏鍙栫浉瀵硅矾寰勶紝鐒跺悗鏍规嵁鐩稿璺緞鍒涘缓涓涓柊鐨刢group缁撴瀯浣撱傛帴鐫浠庡唴鏍镐腑鑾峰彇鍏充簬cgroup鐨勬墍鏈変俊鎭備箣鍚庯紝鑾峰彇CPU鎺у埗鍣紝骞舵牴鎹粍鐨勯厤缃俊鎭洿鏂癱pu.shares鐨勫笺傜劧鍚庯紝濡傛灉姝e湪鎭㈠缁勪笖缁勭殑cpuset鍊间笉涓虹┖锛岃幏鍙朇PUSET鎺у埗鍣紝骞舵牴鎹粍鐨勯厤缃俊鎭洿鏂癱puset鍊笺傛渶鍚庯紝灏嗕慨鏀瑰悗鐨勫煎啓鍏ュ唴鏍搞傚鏋滃嚭鐜板紓甯革紝浼氶噴鏀捐祫婧愬苟杩斿洖 - 1銆傚鏋滀竴鍒囨甯革紝浼氶噴鏀捐祫婧愬苟杩斿洖0銆 +//璇ュ嚱鏁颁富瑕佺敤浜庡湪鏇存柊Cgroup淇℃伅鏃舵牴鎹粍閰嶇疆淇℃伅鐨勫艰繘琛屾洿鏂般備緥濡傦紝褰撻渶瑕佸姩鎬佹敼鍙楥PU鍏变韩鏁伴噺鏃讹紝鍙互閫氳繃璋冪敤璇ュ嚱鏁版洿鏂癈group涓殑cpu.shares鍊笺傚張濡傦紝鍦ㄦ仮澶嶇粍鏃讹紝鍙互閫氳繃璋冪敤璇ュ嚱鏁版洿鏂癈group涓殑cpuset鍊笺 +static int cgexec_update_cgroup_value(gscgroup_grp_t* grp) +{ + char* relpath = NULL; // 鐩稿璺緞 + struct cgroup* cg = NULL; // cgroup缁撴瀯浣 + long cpushares = grp->ainfo.shares; // CPU鍏变韩鏁伴噺 + struct cgroup_controller* cgc_cpu = NULL; // CPU鎺у埗鍣 + int ret; + + /* 鑾峰彇鐩稿璺緞 */ + if (NULL == (relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup))) + return -1; + + /* 鍒嗛厤涓涓柊鐨刢group缁撴瀯浣 */ + cg = cgroup_new_cgroup(relpath); + if (cg == NULL) { + fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); + free(relpath); + relpath = NULL; + return -1; + } + + /* 浠庡唴鏍歌幏鍙栨湁鍏砪group鐨勬墍鏈変俊鎭 */ + ret = cgroup_get_cgroup(cg); + if (ret != 0) { + fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); + free(relpath); + relpath = NULL; + cgroup_free(&cg); + return -1; + } + + /* 鑾峰彇CPU鎺у埗鍣 */ + cgc_cpu = cgroup_get_controller(cg, MOUNT_CPU_NAME); + if (NULL == cgc_cpu) { + fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPU_NAME, grp->grpname); + free(relpath); + relpath = NULL; + cgroup_free(&cg); + return -1; + } + + /* 褰撲负鍔ㄦ佸兼椂锛屾洿鏂癱pu.shares鐨勫 */ + if (0 == cgutil_opt.fixed || cgutil_opt.recover) { + if (cpushares && (0 != (ret = cgroup_set_value_uint64(cgc_cpu, CPU_SHARES, cpushares)))) { + fprintf(stderr, "ERROR: failed to set %s as %ld for %s\n", CPU_SHARES, cpushares, cgroup_strerror(ret)); + goto error; + } + } + + /* 褰撴鍦ㄦ仮澶嶇粍鏃讹紝鍦ㄨ繖閲屾洿鏂癱puset鍊 */ + if (cgutil_opt.recover && grp->cpuset[0]) { + /* 鑾峰彇CPUSET鎺у埗鍣 */ + struct cgroup_controller* cgc_cpus = cgroup_get_controller(cg, MOUNT_CPUSET_NAME); + if (NULL == cgc_cpus) { + fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); + goto error; + } + + /* 浣跨敤鎺у埗鍣ㄨ幏鍙朿puset鍊 */ + if (0 != (ret = cgroup_set_value_string(cgc_cpus, CPUSET_CPUS, grp->cpuset))) { + fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, grp->cpuset, cgroup_strerror(ret)); + goto error; + } + } + + /* 灏嗗间慨鏀瑰埌鍐呮牳涓 */ + if (0 != (ret = cgroup_modify_cgroup(cg))) { + fprintf(stderr, + "ERROR: failed to modify cgroup for %s " + "when updating %s group!\n", + cgroup_strerror(ret), + grp->grpname); + goto error; + } + + cgroup_free_controllers(cg); + cgroup_free(&cg); + + free(relpath); + relpath = NULL; + return 0; + +error: + cgroup_free_controllers(cg); + cgroup_free(&cg); + free(relpath); + relpath = NULL; + return -1; +} +/* + * 鍔熻兘锛氭牴鎹甤lass id鎼滅储workload group id + * 鍙傛暟锛歝ls - class id + * 杩斿洖鍊硷細workload group id + */ +static int cgexec_search_workload_group(int cls) +{ + int i, wd = 0, cmp = -1; + char* tmpstr = strchr(cgutil_opt.wdname, ':'); // 鍦ㄥ瓧绗︿覆cgutil_opt.wdname涓悳绱㈠瓧绗':'鐨勭涓涓嚭鐜颁綅缃紝骞惰繑鍥炶浣嶇疆鐨勬寚閽 + size_t wdname_len = strlen(cgutil_opt.wdname); // 鑾峰彇瀛楃涓瞔gutil_opt.wdname鐨勯暱搴 + + /* 鎼滅储workload group */ + for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { + if (cgutil_vaddr[i]->used == 0 || cgutil_vaddr[i]->ginfo.wd.cgid != cls) + continue; + + /* 鏈夊眰娆$粨鏋勬垨鑰呮棤灞傛缁撴瀯鐨剋orkload鍚嶇О */ + if (tmpstr != NULL) // 鍒ゆ柇瀛楃涓瞭mpstr鏄惁涓虹┖ + cmp = strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname); // 姣旇緝瀛楃涓瞔gutil_vaddr[i]->grpname鍜屽瓧绗︿覆cgutil_opt.wdname + else { + if (':' == cgutil_vaddr[i]->grpname[wdname_len]) // 鍒ゆ柇瀛楃涓瞔gutil_vaddr[i]->grpname鐨勭wdname_len涓瓧绗︽槸鍚︿负':' + cmp = strncmp(cgutil_vaddr[i]->grpname, cgutil_opt.wdname, wdname_len); // 姣旇緝瀛楃涓瞔gutil_vaddr[i]->grpname鐨勫墠wdname_len涓瓧绗﹀拰瀛楃涓瞔gutil_opt.wdname + } + + if (0 == cmp) { + wd = i; + break; + } + } + + return wd; +} + +/* + * 鍔熻兘锛氬湪鎸囧畾璺緞涓婂垱寤轰竴涓猚group锛屾牴鎹粰瀹氱殑鍊艰缃弬鏁 + * 鍙傛暟锛 + * relpath: Cgroup鐨勭浉瀵硅矾寰 + * cpushares: cpu.shares鐨勫 + * ioweight: blkio.weight鐨勫 + * cpuset : cpu.cpus鐨勫 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + * 娉ㄦ剰锛氳鍑芥暟鐢ㄤ簬鍒涘缓鏂扮殑Cgroup銆 + */ +static int cgexec_create_default_cgroup(char* relpath, int cpushares, int ioweight, char* cpuset) +{ + int ret; + struct cgroup* cg = NULL; // cgroup缁撴瀯浣撴寚閽 + struct cgroup_controller* cgc_cpu = NULL; // cgroup鎺у埗鍣ㄧ粨鏋勪綋鎸囬拡 + struct cgroup_controller* cgc_cpuset = NULL; + struct cgroup_controller* cgc_cpuacct = NULL; + + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯浣 */ + cg = cgroup_new_cgroup(relpath); // 鍒涘缓涓涓柊鐨刢group锛屼互relpath涓虹浉瀵硅矾寰 + if (cg == NULL) { + fprintf(stdout, "failed to create the new cgroup for %s\n", relpath); + return -1; + } + + /* 璁剧疆uid鍜実id */ + ret = cgroup_set_uid_gid(cg, + cgutil_passwd_user->pw_uid, + cgutil_passwd_user->pw_gid, + cgutil_passwd_user->pw_uid, + cgutil_passwd_user->pw_gid); + if (ret) { + fprintf(stderr, "ERROR: failed to set uid and gid for %s!\n", cgroup_strerror(ret)); + cgroup_free(&cg); + return -1; + } + + /* 娣诲姞鎺у埗鍣 */ + cgc_cpu = cgroup_add_controller(cg, MOUNT_CPU_NAME); // 鍦╟group涓坊鍔犲悕涓篗OUNT_CPU_NAME鐨勬帶鍒跺櫒 + if (NULL == cgc_cpu) { + fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPU_NAME, relpath); + cgroup_free(&cg); + return -1; + } + + /* 璁剧疆cpu.shares鐨勫 */ + if (cpushares && (0 != (ret = cgroup_set_value_uint64(cgc_cpu, CPU_SHARES, cpushares)))) { + fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPU_SHARES, cpushares, cgroup_strerror(ret)); + + goto error; + } + + /* 璁剧疆cpuset.cpus鐨勫 */ + cgc_cpuset = cgroup_add_controller(cg, MOUNT_CPUSET_NAME); // 鍦╟group涓坊鍔犲悕涓篗OUNT_CPUSET_NAME鐨勬帶鍒跺櫒 + if (NULL == cgc_cpuset) { + fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUSET_NAME, relpath); + goto error; + } + + if (*cpuset) { + /* 璁剧疆cpuset.mems鐨勫 */ + if (0 != (ret = cgroup_set_value_string(cgc_cpuset, CPUSET_MEMS, cgutil_mems))) { + fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUSET_MEMS, 0, cgroup_strerror(ret)); + goto error; + } + + /* 璁剧疆cpuset.cpus鐨勫 */ + if (0 != (ret = cgroup_set_value_string(cgc_cpuset, CPUSET_CPUS, cpuset))) { + fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, cpuset, cgroup_strerror(ret)); + goto error; + } + } + + /* 娣诲姞鎺у埗鍣 */ + cgc_cpuacct = cgroup_add_controller(cg, MOUNT_CPUACCT_NAME); // 鍦╟group涓坊鍔犲悕涓篗OUNT_CPUACCT_NAME鐨勬帶鍒跺櫒 + if (NULL == cgc_cpuacct) { + fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUACCT_NAME, relpath); + goto error; + } + + /* 璁剧疆cpu.usage鐨勫 */ + if (0 != (ret = cgroup_set_value_uint64(cgc_cpuacct, CPUACCT_USAGE, 0))) { + fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUACCT_USAGE, 0, cgroup_strerror(ret)); + + goto error; + } + + /* 鍦ㄥ唴鏍镐腑鍒涘缓Cgroup */ + ret = cgroup_create_cgroup(cg, 0); // 鍦ㄥ唴鏍镐腑鍒涘缓cgroup + if (ret) { + fprintf(stderr, "ERROR: can't create cgroup for %s\n", cgroup_strerror(ret)); + goto error; + } + + cgroup_free_controllers(cg); // 閲婃斁cgroup鐨勬帶鍒跺櫒 + cgroup_free(&cg); // 閲婃斁cgroup + + return 0; + +error: + cgroup_free_controllers(cg); + cgroup_free(&cg); + return -1; +} +/* + * 鍑芥暟鍚嶏細cgexec_create_remain_cgroup + * 鍔熻兘锛氬垱寤哄墿浣欑殑cgroup锛屽熀浜庣浉鍚岀骇鍒殑宸ヤ綔璐熻浇缁 + * 鍙傛暟锛 + * grp锛氬伐浣滆礋杞界粍 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + * 娉ㄦ剰锛氭鍑芥暟鐢ㄤ簬鍒涘缓鏂扮殑Cgroup銆 + */ +static int cgexec_create_remain_cgroup(gscgroup_grp_t* grp) +{ + char* relpath = NULL; // 瀛樺偍鐩稿璺緞鐨勫瓧绗︽寚閽 + long cpushares; + long ioweight; + int i, changed = 0; // 寰幆鍙橀噺鍜屾爣蹇椾綅 + char rempath[16]; // 瀛樺偍鍓╀綑璺緞鐨勫瓧绗︽暟缁 + errno_t sret; + + if (NULL == (relpath = gscgroup_get_parent_wdcg_path(grp->gid, cgutil_vaddr, current_nodegroup))) + return -1; + + /* 娣诲姞鍓╀綑璺緞 */ + sret = snprintf_s( + rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d", GSCGROUP_REMAIN_WORKLOAD, grp->ginfo.wd.wdlevel); + securec_check_intval(sret, free(relpath), -1); + + sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); + securec_check_errno(sret, free(relpath), -1); + + /* 鑾峰彇绫荤兢缁 */ + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->gid == grp->ginfo.wd.cgid) + break; + } + + if (i > CLASSCG_END_ID) { + free(relpath); + relpath = NULL; + return -1; + } + + /* 褰搈axlevel涓1涓擥ROUP_ALL_PERCENT绛変簬cgutil_vaddr[i]->ginfo.cls.rempct鏃讹紝淇敼ginfo.cls.rempct涓篘ORMALWD_PERCENT */ + if (grp->ginfo.cls.maxlevel == 1 && GROUP_ALL_PERCENT == cgutil_vaddr[i]->ginfo.cls.rempct) { + changed = 1; + cgutil_vaddr[i]->ginfo.cls.rempct = NORMALWD_PERCENT; + } + + /* 璁$畻cpushares鍜宨oweight */ + cpushares = MAX_CLASS_CPUSHARES * cgutil_vaddr[i]->ginfo.cls.rempct / GROUP_ALL_PERCENT; + ioweight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_vaddr[i]->ginfo.cls.rempct); + + /* 灏哻puset鍊煎鍒跺埌cgutil_vaddr[i]->cpuset */ + sret = + snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[grp->ginfo.wd.cgid]->cpuset); + securec_check_intval(sret, free(relpath), -1); + + /* 鍒涘缓榛樿cgroup */ + (void)cgexec_create_default_cgroup(relpath, cpushares, ioweight, cgutil_vaddr[i]->cpuset); + + if (changed) + cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; + + free(relpath); + relpath = NULL; + + return 0; +} + +/* + * 鍑芥暟鍚嶏細cgexec_set_blkio_throttle_value + * 鍔熻兘锛氳缃湪鍒涘缓鏂癱group鏃剁殑鍧楄澶嘔O闄愰熷硷紝鍩轰簬閰嶇疆鏂囦欢 + */ +int cgexec_set_blkio_throttle_value(const char* relpath, const char* name, const char* value) +{ + int ret; + char* p = NULL, * q = NULL, * head = NULL, * i = NULL; + struct cgroup* cg = NULL; + struct cgroup_controller* cgc = NULL; + + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯浣 */ + if ((cg = cgexec_get_cgroup(relpath)) == NULL) + return -1; + + /* 鑾峰彇鎺у埗鍣 */ + cgc = cgroup_get_controller(cg, MOUNT_BLKIO_NAME); + if (cgc == NULL) { + cgroup_free(&cg); + return -1; + } + + head = strdup(value); + if (head == NULL) { + cgroup_free_controllers(cg); + cgroup_free(&cg); + return -1; + } + p = head; + + do { + q = strchr(p, '\n'); + if (q != NULL) + *q++ = '\0'; + + i = p; + while (*i++) { + if (*i == '\t') + *i = ' '; + } + + ret = cgroup_set_value_string(cgc, name, p); + if (ret) { + fprintf(stderr, "failed to set %s as %s for %s\n", name, p, cgroup_strerror(ret)); + p = q; + continue; + } + + /* 鏇存柊鎺у埗鍣ㄥ埌鍐呮牳 */ + if (0 != (ret = cgroup_modify_cgroup(cg))) { + fprintf(stderr, + "failed to modify cgroup for %s " + "when modifying values!\n", + cgroup_strerror(ret)); + p = q; + continue; + } + + p = q; + } while (q != NULL); + + free(head); + head = NULL; + cgroup_free_controllers(cg); + cgroup_free(&cg); + + return 0; +} +/* + * function name: cgexec_create_new_cgroup + * 鍔熻兘鍚嶇О锛歝gexec_create_new_cgroup + * description : create the new Cgroup based on configuration information + * 鍔熻兘鎻忚堪锛氭牴鎹厤缃俊鎭垱寤烘柊鐨凜group + * arguments : + * grp: the configuration information + * grp锛氶厤缃俊鎭 + * return value : + * -1: abnormal + * -1锛氬紓甯 + * 0: normal + * 0锛氭甯 + * + * Note: the function is used when creating new Cgroup. + * 娉ㄦ剰锛氳鍑芥暟鐢ㄤ簬鍒涘缓鏂扮殑Cgroup銆 + */ + +int cgexec_create_new_cgroup(gscgroup_grp_t* grp) { + char* relpath = NULL; + int ret; + struct cgroup* cg = NULL; + struct cgroup_controller* cg_controllers[MOUNT_SUBSYS_KINDS] = { 0 }; + + // 浠庨厤缃俊鎭腑鑾峰彇鐩稿璺緞 + if (NULL == (relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup))) + return -1; + + /* allocate new cgroup structure */ + // 鍒嗛厤鏂扮殑cgroup缁撴瀯 + cg = cgroup_new_cgroup(relpath); + if (cg == NULL) { + fprintf(stdout, "failed to create the new cgroup for %s\n", relpath); + free(relpath); + relpath = NULL; + return -1; + } + + /* set the uid and gid */ + // 璁剧疆uid鍜実id + ret = cgroup_set_uid_gid(cg, + cgutil_passwd_user->pw_uid, + cgutil_passwd_user->pw_gid, + cgutil_passwd_user->pw_uid, + cgutil_passwd_user->pw_gid); + if (ret) { + fprintf(stderr, "ERROR: failed to set uid and gid for %s!\n", cgroup_strerror(ret)); + free(relpath); + relpath = NULL; + cgroup_free(&cg); + return -1; + } + + /* add the controller */ + // 娣诲姞鎺у埗鍣 + cg_controllers[MOUNT_CPU_ID] = cgroup_add_controller(cg, MOUNT_CPU_NAME); + if (NULL == cg_controllers[MOUNT_CPU_ID]) { + fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPU_NAME, grp->grpname); + free(relpath); + relpath = NULL; + cgroup_free(&cg); + return -1; + } + + /* set the cpu.shares value */ + // 璁剧疆cpu.shares鍊 + if (grp->ainfo.shares && + (0 != (ret = cgroup_set_value_uint64(cg_controllers[MOUNT_CPU_ID], CPU_SHARES, grp->ainfo.shares)))) { + fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPU_SHARES, grp->ainfo.shares, cgroup_strerror(ret)); + goto error; + } + + cg_controllers[MOUNT_CPUSET_ID] = cgroup_add_controller(cg, MOUNT_CPUSET_NAME); + if (NULL == cg_controllers[MOUNT_CPUSET_ID]) { + fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUSET_NAME, grp->grpname); + goto error; + } + + /* set the cpu.cpus value */ + // 璁剧疆cpu.cpus鍊 + if (*grp->cpuset) { + if ((0 != (ret = cgroup_set_value_string(cg_controllers[MOUNT_CPUSET_ID], CPUSET_MEMS, cgutil_mems)))) { + fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUSET_MEMS, 0, cgroup_strerror(ret)); + goto error; + } + + if (0 != (ret = cgroup_set_value_string(cg_controllers[MOUNT_CPUSET_ID], CPUSET_CPUS, grp->cpuset))) { + fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, grp->cpuset, cgroup_strerror(ret)); + goto error; + } + } + + /* add cpuacct controllor */ + // 娣诲姞cpuacct鎺у埗鍣 + cg_controllers[MOUNT_CPUACCT_ID] = cgroup_add_controller(cg, MOUNT_CPUACCT_NAME); + if (NULL == cg_controllers[MOUNT_CPUACCT_ID]) { + fprintf(stderr, "ERROR: failed to add %s controller for %s!\n", MOUNT_CPUACCT_NAME, grp->grpname); + goto error; + } + + if (0 != (ret = cgroup_set_value_int64(cg_controllers[MOUNT_CPUACCT_ID], CPUACCT_USAGE, 0))) { + fprintf(stderr, "ERROR: failed to set %s as %d for %s\n", CPUACCT_USAGE, 0, cgroup_strerror(ret)); + goto error; + } + + /* create the Cgroup on kernel */ + // 鍦ㄥ唴鏍镐腑鍒涘缓Cgroup + ret = cgroup_create_cgroup(cg, 0); + if (ret) { + fprintf(stderr, "ERROR: can't create cgroup for %s\n", cgroup_strerror(ret)); + goto error; + } + + cgroup_free_controllers(cg); + cgroup_free(&cg); + + free(relpath); + relpath = NULL; + return 0; + +error: + free(relpath); + relpath = NULL; + cgroup_free_controllers(cg); + cgroup_free(&cg); + return -1; +} +/* + * function name: cgexec_create_workload_cgroup + * description : 鏍规嵁閰嶇疆淇℃伅鍒涘缓鏂扮殑Cgroup + * arguments : + * grp: 宸ヤ綔璐熻浇缁勭殑閰嶇疆淇℃伅 + * return value : + * -1: 寮傚父 + * 0: 姝e父 + * + * Note: 鍦ㄥ垱寤烘柊鐨勫伐浣滆礋杞紺group鏃朵娇鐢ㄦ鍑芥暟銆 + */ +static int cgexec_create_workload_cgroup(gscgroup_grp_t* grp) +{ + int cgid = grp->ginfo.wd.cgid; + gscgroup_grp_t* cls_grp = cgutil_vaddr[cgid]; + int nextlevel = cls_grp->ginfo.cls.maxlevel + 1; + int i, j; + + /* 璺宠繃宸ヤ綔璐熻浇 */ + if (nextlevel > grp->ginfo.wd.wdlevel) + return 0; + /* 褰撳伐浣滆礋杞芥槸涓嬩竴涓骇鍒殑宸ヤ綔璐熻浇缁勬椂 */ + if (nextlevel == grp->ginfo.wd.wdlevel) { + if (-1 == cgexec_create_new_cgroup(grp)) + return -1; + + if (-1 == cgexec_create_remain_cgroup(grp)) + return -1; + } + /* 闇瑕侀鍏堝垱寤烘墍鏈夌埗绾у伐浣滆礋杞界粍 */ + else if (nextlevel < grp->ginfo.wd.wdlevel) { + cls_grp->ginfo.cls.rempct += grp->ginfo.wd.percent; + + for (i = nextlevel; i < grp->ginfo.wd.wdlevel; i++) { + for (j = grp->gid; j <= WDCG_END_ID; j++) { + if (cgutil_vaddr[j]->used && cgid == cgutil_vaddr[j]->ginfo.wd.cgid && + i == cgutil_vaddr[j]->ginfo.wd.wdlevel) + break; + } + + if (j > WDCG_END_ID) { + fprintf(stderr, "can't find the parent workload!\n"); + return -1; + } + + if (-1 == cgexec_create_new_cgroup(cgutil_vaddr[j])) + return -1; + + cls_grp->ginfo.cls.rempct -= cgutil_vaddr[j]->ginfo.wd.percent; + + if (-1 == cgexec_create_remain_cgroup(cgutil_vaddr[j])) + return -1; + + /* 璁剧疆绫荤粍鐨勬渶澶х骇鍒 */ + cls_grp->ginfo.cls.maxlevel = i; + } + + if (-1 == cgexec_create_new_cgroup(grp)) + return -1; + + cls_grp->ginfo.cls.rempct -= grp->ginfo.wd.percent; + + if (-1 == cgexec_create_remain_cgroup(grp)) + return -1; + } + + /* 璁剧疆绫荤粍鐨勬渶澶х骇鍒 */ + cgutil_vaddr[cgid]->ginfo.cls.maxlevel += 1; + + return 0; +} + +/* + * function name: cgexec_create_timeshare_cgroup + * description : 鍒涘缓鎸囧畾绫荤粍鐨勬墍鏈夋椂闂村叡浜獵group + * arguments : + * grp: 绫荤粍鐨勯厤缃俊鎭 + * return value : + * -1: 寮傚父 + * 0: 姝e父 + * + * Note: 鍦ㄥ垱寤烘柊鐨勭被缁勬椂浣跨敤姝ゅ嚱鏁般 + */ +static int cgexec_create_timeshare_cgroup(gscgroup_grp_t* grp) +{ + char* toppath = NULL; + char* relpath = NULL; + long cpushares; + long ioweight; + int j, ret; + + /* 鍒涘缓椤剁骇鏃堕棿鍏变韩Cgroup */ + cpushares = DEFAULT_CPU_SHARES; + ioweight = DEFAULT_IO_WEIGHT; + + /* 鑾峰彇椤剁骇鏃堕棿鍏变韩璺緞 */ + toppath = gscgroup_get_topts_path(grp->gid, cgutil_vaddr, current_nodegroup); + if (NULL == toppath) + return -1; + + /* 鍒涘缓椤剁骇Cgroup */ + ret = cgexec_create_default_cgroup(toppath, cpushares, ioweight, grp->cpuset); + if (-1 == ret) { + free(toppath); + toppath = NULL; + return -1; + } + + /* 涓烘椂闂村叡浜獵group璺緞鍒嗛厤鍐呭瓨 */ + if (NULL == (relpath = (char*)malloc(GPNAME_PATH_LEN))) { + fprintf(stderr, "ERROR: failed to allocate memory for path!\n"); + free(toppath); + toppath = NULL; + return -1; + } + + /* 鍒涘缓榛樿鏃堕棿鍏变韩Cgroup */ + for (j = TSCG_START_ID; j <= TSCG_END_ID; j++) { + int rc = snprintf_s(relpath, GPNAME_PATH_LEN, GPNAME_PATH_LEN - 1, "%s/%s", toppath, cgutil_vaddr[j]->grpname); + securec_check_intval(rc, free(toppath); free(relpath), -1); + + cpushares = cgutil_vaddr[j]->ainfo.shares; + ioweight = cgutil_vaddr[j]->ainfo.weight; + + ret = cgexec_create_default_cgroup(relpath, cpushares, ioweight, grp->cpuset); + if (-1 == ret) { + free(toppath); + toppath = NULL; + free(relpath); + relpath = NULL; + return -1; + } + } + + free(toppath); + toppath = NULL; + free(relpath); + relpath = NULL; + + return 0; +} +/* + * 鍑芥暟鍚嶇О锛歝gexec_delete_default_cgroup + * 鍔熻兘锛氭牴鎹厤缃俊鎭垹闄group + * 鍙傛暟锛 + * 聽 聽 聽 聽grp锛欸roup鐨勯厤缃俊鎭 + * 杩斿洖鍊硷細 + * 聽 聽 聽 聽-1锛氬紓甯 + * 聽 聽 聽 聽0锛氭甯 + * + * 娉ㄦ剰锛氬綋鍒犻櫎Cgroup鏃朵娇鐢ㄨ鍑芥暟銆 + */ +static int cgexec_delete_default_cgroup(gscgroup_grp_t* grp) +{ + char* relpath = NULL; + + // 鑾峰彇鐩稿璺緞 + relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup); + if (NULL == relpath) + return -1; + + // 鍒犻櫎Cgroups + (void)cgexec_delete_cgroups(relpath); + + free(relpath); + relpath = NULL; + + return 0; +} + +/* + * 鍑芥暟鍚嶇О锛歝gexec_create_nodegroup_default_cgroups + * 鍔熻兘锛氭牴鎹妭鐐圭粍鍚嶅垱寤洪粯璁ょ殑Cgroups + * 杩斿洖鍊硷細 + * 聽 聽 聽 聽-1锛氬紓甯 + * 聽 聽 聽 聽0锛氭甯 + * + */ +static int cgexec_create_nodegroup_default_cgroups(void) +{ + int i, ret = 0; + errno_t sret; + char* cpuset = NULL; + char cpu_allset[CPUSET_LEN] = { 0 }; + + /* 鑾峰彇鍐呭瓨闆嗗悎 */ + if (-1 == cgexec_get_cgroup_cpuset_info(TOPCG_GAUSSDB, &cpuset)) { + fprintf(stderr, "閿欒锛氬湪鍒涘缓榛樿鐨勮妭鐐圭粍Cgroups鏃讹紝鑾峰彇cpusets鍜宮ems澶辫触銆俓n"); + return -1; + } + + /* 璁剧疆gaussdb鐨勯粯璁puset鍊 */ + if ((cpuset != NULL) && *cpuset != '\0') { + sret = snprintf_s(cpu_allset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); + securec_check_intval(sret, free(cpuset), -1); + free(cpuset); + cpuset = NULL; + } + else { + sret = snprintf_s(cpu_allset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); + securec_check_intval(sret, , -1); + } + + sret = snprintf_s(cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpu_allset); + securec_check_intval(sret, , -1); + + /* 鍒涘缓鑺傜偣缁凜group */ + if (-1 == (ret = cgexec_create_new_cgroup(cgutil_vaddr[TOPCG_CLASS]))) { + fprintf(stderr, "閿欒锛氬垱寤%s cgroup澶辫触锛乗n", cgutil_vaddr[TOPCG_CLASS]->grpname); + return -1; + } + + /* 鍒涘缓闄imeshare Cgroup澶栫殑鎵鏈塁group */ + for (i = CLASSCG_START_ID; i <= WDCG_END_ID; i++) { + if (0 == cgutil_vaddr[i]->used) + continue; + + /* 鏇存柊cpuset淇℃伅 */ + sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpu_allset); + securec_check_intval(sret, , -1); + + if (i >= CLASSCG_START_ID && i <= CLASSCG_END_ID) { + /* 閲嶇疆鏈澶х骇鍒暟 */ + cgutil_vaddr[i]->ginfo.cls.maxlevel = 0; + cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; + + ret = cgexec_create_new_cgroup(cgutil_vaddr[i]); + } + else if (i >= WDCG_START_ID && i <= WDCG_END_ID) { + int cls = cgutil_vaddr[i]->ginfo.wd.cgid; + + if (strncmp(cgutil_vaddr[i]->grpname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1) == 0) + cgconf_set_top_workload_group(i, cls); + + if (cgutil_vaddr[i]->ginfo.cls.maxlevel == 1) + cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; + else if (cgutil_vaddr[cls]->ginfo.cls.maxlevel < cgutil_vaddr[i]->ginfo.wd.wdlevel) + cgutil_vaddr[cls]->ginfo.cls.rempct -= cgutil_vaddr[i]->ginfo.cls.percent; + + ret = cgexec_create_workload_cgroup(cgutil_vaddr[i]); + } + + if (-1 == ret) { + fprintf(stderr, "閿欒锛氬垱寤%s cgroup澶辫触锛乗n", cgutil_vaddr[i]->grpname); + continue; + } + } + + /* 涓烘瘡涓狢lass group鍒涘缓timeshare group */ + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + if (0 == cgutil_vaddr[i]->used) + continue; + + ret = cgexec_create_timeshare_cgroup(cgutil_vaddr[i]); + if (-1 == ret) { + fprintf(stderr, "閿欒锛氫负%s鍒涘缓timeshare cgroup澶辫触锛乗n", cgutil_vaddr[i]->grpname); + return -1; + } + } + + return ret; +} +/** + * function name: cgexec_create_default_cgroups + * description: 濡傛灉鍐呮牳涓婃病鏈塁groups锛屽垯闇瑕佹牴鎹粯璁ょ殑閰嶇疆鏂囦欢鏉ュ垱寤篊groups銆 + * return value: + * -1: 寮傚父 + * 0: 姝e父 + * + */ +static int cgexec_create_default_cgroups(void) +{ + int i, ret = 0; + errno_t sret; + + /* 鍦ㄦ寕杞紺group鏂囦欢绯荤粺鍚庯紝鏍笴group宸插瓨鍦 */ + if ((cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) && (cgutil_opt.refresh == 0 && cgutil_opt.revert == 0)) + (void)cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_ROOT]); + + /* 鍒涘缓闄imeshare Cgroup涔嬪鐨勬墍鏈塁group */ + for (i = 1; i <= WDCG_END_ID; i++) { + if (0 == cgutil_vaddr[i]->used) + continue; + + /* 璁剧疆gaussdb榛樿鐨刢puset鍊 */ + if (*cgutil_vaddr[TOPCG_GAUSSDB]->cpuset == '\0') { + sret = snprintf_s(cgutil_vaddr[TOPCG_GAUSSDB]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); + securec_check_intval(sret, , -1); + } + + /* Top Cgroup */ + // 濡備綍澶勭悊閰嶉鍜宑puset锛 + // 1. 濡傛灉Gaussdb鑼冨洿鏀瑰彉锛屾墍鏈夊瓙鐩綍鐨勯厤棰濋兘搴旇鏀瑰彉 + // 鎵浠gexec_check_top_cpuset鍑芥暟鏃犳硶澶勭悊杩欎釜闂 + // 2. 濡傛灉cpusets涓庝笂绾х洰褰曚笉鍚岋紝搴旇璁$畻閰嶉鍊 + + if (i > TOPCG_GAUSSDB && *cgutil_vaddr[i]->cpuset == '\0') { + sret = snprintf_s( + cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); + securec_check_intval(sret, , -1); + } + + if (i < CLASSCG_START_ID) + ret = cgexec_create_new_cgroup(cgutil_vaddr[i]); + else if (i >= CLASSCG_START_ID && i <= CLASSCG_END_ID) { + if (0 == cgutil_vaddr[i]->used) + continue; + + /* 閲嶇疆鏈澶у眰绾ф暟 */ + cgutil_vaddr[i]->ginfo.cls.maxlevel = 0; + cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; + + ret = cgexec_create_new_cgroup(cgutil_vaddr[i]); + } + else if (i >= WDCG_START_ID && i <= WDCG_END_ID) { + int cls = cgutil_vaddr[i]->ginfo.wd.cgid; + + if (strncmp(cgutil_vaddr[i]->grpname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1) == 0) + cgconf_set_top_workload_group(i, cls); + + if (cgutil_vaddr[i]->ginfo.cls.maxlevel == 1) + cgutil_vaddr[i]->ginfo.cls.rempct = GROUP_ALL_PERCENT; + else if (cgutil_vaddr[cls]->ginfo.cls.maxlevel < cgutil_vaddr[i]->ginfo.wd.wdlevel) + cgutil_vaddr[cls]->ginfo.cls.rempct -= cgutil_vaddr[i]->ginfo.cls.percent; + + ret = cgexec_create_workload_cgroup(cgutil_vaddr[i]); + } + + if (-1 == ret) { + fprintf(stderr, "failed to create %s cgroup + } + } + + return ret; +} + +/* + * function name: cgexec_delete_remain_cgroup + * 鍔熻兘锛氬綋涓涓伐浣滆礋杞紺group琚垹闄ゆ椂锛岄渶瑕佽皟鐢ㄦ鍑芥暟鏉ュ垹闄ゆ渶鍚庡墿浣欑殑Cgroup鍙婂叾瀛怌group锛坱imeshare Cgroup锛 + * 鍙傛暟锛 + * grp: 涓庡墿浣機group鐩稿悓绾у埆鐨勫伐浣滆礋杞紺group鐨勯厤缃 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + */ +static int cgexec_delete_remain_cgroup(gscgroup_grp_t* grp) +{ + char* relpath = NULL; // 鐩稿璺緞 + char rempath[16]; // 鍓╀綑璺緞 + errno_t sret; + + relpath = gscgroup_get_parent_wdcg_path(grp->gid, cgutil_vaddr, current_nodegroup); // 鑾峰彇鐖惰矾寰 + if (NULL == relpath) + return -1; + + /* 娣诲姞鍓╀綑璺緞鐩綍 */ + sret = snprintf_s( + rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d", GSCGROUP_REMAIN_WORKLOAD, grp->ginfo.wd.wdlevel); + securec_check_intval(sret, free(relpath), -1); // 妫鏌ュ瓧绗︿覆鎷兼帴鐨勭粨鏋滄槸鍚﹀紓甯 + sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); // 瀛楃涓叉嫾鎺 + securec_check_errno(sret, free(relpath), -1); + + (void)cgexec_delete_cgroups(relpath); // 鍒犻櫎鎸囧畾璺緞涓嬬殑Cgroups + + free(relpath); // 閲婃斁鍐呭瓨 + relpath = NULL; + + return 0; +} + +/* + * function name: cgexec_copy_next_level_cgroup + * 鍔熻兘锛氬皢鎸囧畾鐨勫伐浣滆礋杞紺group澶嶅埗鍒颁笂涓绾 + * 鍙傛暟锛 + * relpath: 鍒犻櫎Cgroup鐨勭埗璺緞 + * grp: 宸ヤ綔璐熻浇Cgroup鐨勯厤缃 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + */ +int cgexec_copy_next_level_cgroup(const char* relpath, gscgroup_grp_t* grp) +{ + int ret; + char grpname[GPNAME_LEN]; // Cgroup鍚嶇О + char* wdpath = NULL; // 宸ヤ綔璺緞 + char* p = NULL; // 涓存椂鍙橀噺 + struct cgroup* oldcg = NULL, * newcg = NULL; // cgroup缁撴瀯浣 + struct cgroup_controller* cgc[MOUNT_SUBSYS_KINDS]; // cgroup鎺у埗鍣 + errno_t sret; + + /* 鑾峰彇鎸囧畾Cgroup鐨勫綋鍓嶈矾寰 */ + wdpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup); + if (NULL == wdpath) { + fprintf(stderr, "ERROR: failed to allocate memory for path!\n"); + return -1; + } + + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯浣 */ + oldcg = cgroup_new_cgroup(wdpath); + if (oldcg == NULL) { + fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", wdpath); + free(wdpath); + wdpath = NULL; + return -1; + } + + /* 浠庡唴鏍歌幏鍙栨湁鍏砪group鐨勬墍鏈変俊鎭 */ + ret = cgroup_get_cgroup(oldcg); + if (ret != 0) { + fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", wdpath, cgroup_strerror(ret), ret); + free(wdpath); + wdpath = NULL; + cgroup_free(&oldcg); + return -1; + } + + sret = memset_s(wdpath, GPNAME_PATH_LEN, 0, GPNAME_PATH_LEN); + securec_check_errno(sret, free(wdpath); cgroup_free(&oldcg), -1); // 妫鏌ュ唴瀛樻竻闆剁殑缁撴灉鏄惁寮傚父 + + /* 鑾峰彇涓嶅甫绾у埆鐨刧rpname */ + sret = strcpy_s(grpname, GPNAME_LEN, grp->grpname); + securec_check_errno( // 妫鏌ュ瓧绗︿覆鎷疯礉鐨勭粨鏋滄槸鍚﹀紓甯 + sret, free(wdpath); cgroup_free(&oldcg), -1); + + ... +} +/* + * 鍑芥暟鍚嶇О锛歝gexec_delete_workload_cgroup + * 鎻忚堪锛氬垹闄ゆ寚瀹氱殑宸ヤ綔璐熻浇Cgroup + * 鍙傛暟锛 + * grp锛氬伐浣滆礋杞界粍鐨勯厤缃 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + */ + +static int cgexec_delete_workload_cgroup(gscgroup_grp_t* grp) +{ + int wgid = grp->gid; // 宸ヤ綔璐熻浇缁勭殑ID + int wglevel = grp->ginfo.wd.wdlevel; // 宸ヤ綔璐熻浇缁勭殑灞傜骇 + int cgid = grp->ginfo.wd.cgid; // 鎺у埗缁処D + gscgroup_grp_t* cls_grp = cgutil_vaddr[cgid]; // 鎺у埗缁勭殑铏氭嫙鍦板潃 + int i, j, ret, rempct = GROUP_ALL_PERCENT; // i, j涓哄惊鐜彉閲忥紝ret涓鸿繑鍥炲硷紝rempct涓哄墿浣欑櫨鍒嗘瘮 + int cpushares, ioweight; // cpushares涓篊PU浠介锛宨oweight涓篒O鏉冮噸 + char* relpath = NULL; // 鐩稿璺緞 + char rempath[16]; // 鍓╀綑璺緞 + errno_t sret; + + /* 濡傛灉鏄渶鍚庝竴绾х殑宸ヤ綔璐熻浇缁 */ + if (wglevel == cls_grp->ginfo.cls.maxlevel) { + /* 鍒犻櫎鍓╀綑鐨勬帶鍒剁粍 */ + if (-1 == cgexec_delete_remain_cgroup(grp)) + return -1; + + /* 鍒犻櫎宸ヤ綔璐熻浇Cgroup */ + if (-1 == cgexec_delete_default_cgroup(grp)) + return -1; + + /* 閲嶇疆鍓╀綑鐧惧垎姣 */ + cls_grp->ginfo.cls.rempct += grp->ginfo.wd.percent; + + cgconf_reset_workload_group(grp->gid); + } + else { + /* 鍒犻櫎绗竴涓 */ + if (-1 == cgexec_delete_default_cgroup(grp)) + return -1; + + if (NULL == (relpath = gscgroup_get_parent_wdcg_path(grp->gid, cgutil_vaddr, current_nodegroup))) + return -1; + + /* 浠2寮濮嬭鏁版槸涓轰簡涓㈠純鎺塗opWD缁 */ + for (i = 2; i < wglevel; i++) { + /* 璁$畻鍓╀綑鐧惧垎姣 */ + for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { + if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cgid && + cgutil_vaddr[j]->ginfo.wd.wdlevel == i) + break; + } + + rempct -= cgutil_vaddr[j]->ginfo.wd.percent; + } + + cls_grp->ginfo.cls.rempct += grp->ginfo.wd.percent; + + /* 閲嶇疆锛屼笉鑳戒娇鐢╣rp */ + cgconf_reset_workload_group(wgid); + + for (i = wglevel; i < cls_grp->ginfo.cls.maxlevel; i++) { + /* 鑾峰彇涓嬩竴灞傜骇鐨勫伐浣滆礋杞 */ + for (j = WDCG_START_ID; j <= WDCG_END_ID; j++) { + if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cgid && + cgutil_vaddr[j]->ginfo.wd.wdlevel == (i + 1)) + break; + } + + /* 灏嗕笅涓涓伐浣滆礋杞藉鍒跺埌杩欎竴涓眰绾 */ + if (-1 == cgexec_copy_next_level_cgroup(relpath, cgutil_vaddr[j])) { + free(relpath); + relpath = NULL; + return -1; + } + + /* 娣诲姞鍓╀綑璺緞 */ + sret = snprintf_s(rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d/", GSCGROUP_REMAIN_WORKLOAD, i); + securec_check_intval(sret, free(relpath), -1); + sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); + securec_check_errno(sret, free(relpath), -1); + + /* 鏇存柊鍓╀綑鎺у埗缁 */ + rempct -= cgutil_vaddr[j]->ginfo.wd.percent; + + cpushares = MAX_CLASS_CPUSHARES * rempct / GROUP_ALL_PERCENT; + ioweight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, rempct); + + ret = cgexec_update_remain_value(relpath, cpushares, ioweight); + if (-1 == ret) { + free(relpath); + relpath = NULL; + return -1; + } + } + + /* 娣诲姞鍓╀綑璺緞 */ + sret = snprintf_s(rempath, sizeof(rempath), sizeof(rempath) - 1, "%s:%d/", GSCGROUP_REMAIN_WORKLOAD, i); + securec_check_intval(sret, free(relpath), -1); + sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); + securec_check_errno(sret, free(relpath), -1); + + (void)cgexec_delete_cgroups(relpath); + free(relpath); + relpath = NULL; + } + + cls_grp->ginfo.cls.maxlevel -= 1; + + if (-1 == cgexec_create_timeshare_cgroup(cgutil_vaddr[cgid])) + return -1; + + return 0; +} +/* + * 鍑芥暟鍚嶇О锛歝gexec_delete_class_cgroup + * 鍔熻兘鎻忚堪锛氭牴鎹夐」鍒犻櫎绫籆group鍜屽伐浣滆礋杞紺group + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ +static int cgexec_delete_class_cgroup(void) +{ + int i, cls = 0, wd = 0; + + /* 妫鏌ョ被鏄惁瀛樺湪 */ + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0) + continue; + + // 姣旇緝绫诲悕绉版槸鍚︿笌閫夐」涓殑绫诲悕鐩稿悓 + if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { + cls = i; + break; + } + } + + /* 澶囦唤閰嶇疆鏂囦欢浠ヤ究鎭㈠ */ + if (-1 == cgconf_backup_config_file()) { + return -1; + } + + if (cls) { + if (cgutil_opt.wdname[0]) { + wd = cgexec_search_workload_group(cls); + + if (wd) { + (void)cgexec_delete_workload_cgroup(cgutil_vaddr[wd]); + } + else { + fprintf(stderr, "ERROR: the specified workload %s doesn't exist!\n", cgutil_opt.wdname); + return -1; + } + } + else { + if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls])) + return -1; + + cgconf_reset_class_group(cls); + } + } + else { + fprintf(stderr, "ERROR: the specified class %s doesn't exist!\n", cgutil_opt.clsname); + return -1; + } + + return 0; +} + +/* + * 鍑芥暟鎻忚堪锛氭洿鏂癱group cpuset鍊笺 + * @IN relpath锛歝group鐨勭浉瀵硅矾寰 + * @IN cpuset锛歝puset鍊 + * 杩斿洖鍊硷細-1锛氬紓甯 0锛氭甯 + * 鍙傝冿細 + */ +static int cgexec_update_cgroup_cpuset_value(char* relpath, char* cpuset) +{ + int ret; + struct cgroup_controller* cgc = NULL; + struct cgroup* cg = NULL; + + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯浣 */ + cg = cgroup_new_cgroup(relpath); + if (cg == NULL) { + fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); + return -1; + } + + /* 浠庡唴鏍歌幏鍙栨湁鍏砪group鐨勬墍鏈変俊鎭 */ + ret = cgroup_get_cgroup(cg); + if (ret != 0) { + fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); + cgroup_free(&cg); + return -1; + } + + /* 鑾峰彇CPUSET鎺у埗鍣 */ + cgc = cgroup_get_controller(cg, MOUNT_CPUSET_NAME); + if (NULL == cgc) { + cgroup_free(&cg); + return -1; + } + + /* 浣跨敤鎺у埗鍣ㄨ幏鍙朿puset鍊 */ + if (0 != (ret = cgroup_set_value_string(cgc, CPUSET_CPUS, cpuset))) { + fprintf(stderr, "ERROR: failed to set %s as %s for %s\n", CPUSET_CPUS, cpuset, cgroup_strerror(ret)); + goto error; + } + + /* 淇敼鍐呮牳涓殑鍊 */ + if (0 != (ret = cgroup_modify_cgroup(cg))) { + fprintf(stderr, + "ERROR: failed to modify cgroup for %s " + "when updating group!\n", + cgroup_strerror(ret)); + goto error; + } + + cgroup_free_controllers(cg); + cgroup_free(&cg); + + return 0; + +error: + cgroup_free_controllers(cg); + cgroup_free(&cg); + return -1; +} +/** + * @Description: 鏇存柊鏃堕棿鍏变韩缁勭殑cpuset鍊笺 + * @IN grp: 缁勪俊鎭 + * @IN cpuset: cpuset鍊 + * @IN update: 0锛氬厛鏇存柊淇濈暀鐨勬帶鍒剁粍鐨刢puset鍊硷紝鍐嶆洿鏂版椂闂村叡浜粍锛涢潪0锛氬厛鏇存柊鏃堕棿鍏变韩缁勭殑cpuset鍊硷紝鍐嶆洿鏂颁繚鐣欑殑缁 + * @Return: -1锛氬紓甯革紱0锛氭甯 + * @See also: + */ +static int cgexec_update_timeshare_cpuset(gscgroup_grp_t* grp, char* cpuset, unsigned char update) +{ + char* toppath = NULL; // 椤剁骇timeshare璺緞 + char* relpath = NULL; // timeshare cgroup鐨勭浉瀵硅矾寰 + int i; + + /* 鑾峰彇椤剁骇timeshare璺緞 */ + toppath = gscgroup_get_topts_path(grp->gid, cgutil_vaddr, current_nodegroup); // 鑾峰彇椤剁骇timeshare璺緞 + if (NULL == toppath) + return -1; + + /* 濡傛灉update鏍囧織涓0锛屽垯蹇呴』鍏堜娇鐢╟puset鍊兼洿鏂皌oppath cgroup */ + if (update == 0 && cgexec_update_cgroup_cpuset_value(toppath, cpuset) == -1) { // 鏇存柊toppath cgroup鐨刢puset鍊 + fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); + free(toppath); + toppath = NULL; + return -1; + } + + /* 涓簍imeshare cgroup鐨勮矾寰勫垎閰嶅唴瀛 */ + if (NULL == (relpath = (char*)malloc(GPNAME_PATH_LEN))) { + fprintf(stderr, "ERROR: failed to allocate memory for path!\n"); + free(toppath); + toppath = NULL; + return -1; + } + + /* 鏇存柊鎵鏈塼imeshare缁勭殑cpuset鍊 */ + for (i = TSCG_START_ID; i <= TSCG_END_ID; ++i) { + int rc = snprintf_s(relpath, GPNAME_PATH_LEN, GPNAME_PATH_LEN - 1, "%s/%s", toppath, cgutil_vaddr[i]->grpname); // 鏋勫缓timeshare cgroup鐨勮矾寰 + securec_check_intval(rc, free(toppath); free(relpath), -1); + + if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { // 鏇存柊timeshare缁勭殑cpuset鍊 + fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); + goto error; + } + } + + /* 濡傛灉update涓嶄负0锛屽垯鍏堟洿鏂皌imeshare缁勶紝鐒跺悗鍐嶆洿鏂皌oppath缁 */ + if (update && cgexec_update_cgroup_cpuset_value(toppath, cpuset) == -1) { // 濡傛灉update涓嶄负0锛屽垯鍏堟洿鏂皌imeshare缁勶紝鐒跺悗鍐嶆洿鏂皌oppath缁 + fprintf(stderr, "ERROR: failed to add %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); + goto error; + } + + free(toppath); + toppath = NULL; + free(relpath); + relpath = NULL; + + return 0; + +error: + free(toppath); + toppath = NULL; + free(relpath); + relpath = NULL; + return -1; +} + +/** + * @Description: 鏇存柊缁勭殑cpuset鍊笺 + * @IN grp: 缁勪俊鎭 + * @IN cpuset: cpuset鍊 + * @Return: -1锛氬紓甯革紱0锛氭甯 + * @See also: + */ +static int cgexec_update_cgroup_cpuset(gscgroup_grp_t* grp, char* cpuset) +{ + char* relpath = NULL; // 鐩稿璺緞 + + if (strcmp(grp->cpuset, cpuset) == 0) // 濡傛灉cpuset鍊肩浉鍚岋紝鍒欎笉鎵ц鏇存柊鎿嶄綔 + return 0; + + /* 鑾峰彇鐩稿璺緞 */ + if (NULL == (relpath = gscgroup_get_relative_path(grp->gid, cgutil_vaddr, current_nodegroup))) + return -1; + + /* 浣跨敤鐩稿璺緞鏇存柊cgroup鐨刢puset鍊 */ + if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { + fprintf(stderr, "ERROR: failed to update %s controller in %s!\n", MOUNT_CPUSET_NAME, grp->grpname); + free(relpath); + relpath = NULL; + return -1; + } + + /* 灏嗘柊鍊间繚瀛樹负绫荤殑cpuset鍊 */ + errno_t sret = snprintf_s(grp->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); + securec_check_intval(sret, free(relpath), -1); + + free(relpath); + relpath = NULL; + + return 0; +} +/** + * @Description: 鏇存柊'topwd'缁勭殑cpuset鍊笺 + * @IN cls: 鍒嗙被缁刬d + * @IN cpuset: cpuset鍊 + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ +static int cgexec_update_topwd_cgroup_cpuset(int cls, char* cpuset) +{ + char* relpath = NULL; + int i; + char topwd[GPNAME_LEN]; + + /* 鑾峰彇'topwd'缁勭殑瀹屾暣鍚嶇О*/ + errno_t rc = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); + securec_check_intval(rc, , -1); + + /* 璁剧疆椤剁骇宸ヤ綔璐熻浇椤 */ + for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { + if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->ginfo.wd.cgid == cls && + strcmp(cgutil_vaddr[i]->grpname, topwd) == 0) + break; + } + + /* 鎵句笉鍒'topwd'缁 */ + if (i > WDCG_END_ID) { + fprintf(stderr, "ERROR: Cannot find topwd for class: %s\n", cgutil_vaddr[i]->grpname); + return -1; + } + + /* 鑾峰彇鐩稿璺緞 */ + if (NULL == (relpath = gscgroup_get_relative_path(cgutil_vaddr[i]->gid, cgutil_vaddr, current_nodegroup))) + return -1; + + /* 浣跨敤鐩稿璺緞鏇存柊缁刢puset鍊 */ + if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { + fprintf(stderr, + "ERROR: failed to add %s controller in %s:%s!\n", + MOUNT_CPUSET_NAME, + cgutil_vaddr[cls]->grpname, + topwd); + free(relpath); + relpath = NULL; + return -1; + } + + /* 灏嗘柊鍊间繚瀛樹负鍒嗙被cpuset鍊 */ + errno_t sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); + securec_check_intval(sret, free(relpath); , -1); + + free(relpath); + relpath = NULL; + + return 0; +} + +/** + * @Description: 妫鏌ョ粰瀹氬垎绫荤殑鎵鏈夊伐浣滆礋杞界粍鐨刢puset鍊笺 + * @IN cls: 鍒嗙被缁刬d + * @IN cpuset: cpuset鍊 + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ +static int cgexec_check_workload_cgroup_cpuset(int cls, const char* cpuset) +{ + int i; + char topwd[GPNAME_LEN]; + + /* 鑾峰彇'topwd'鐨勫畬鏁村悕绉 */ + errno_t rc = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); + securec_check_intval(rc, , -1); + + /* 璁剧疆鎵鏈夊伐浣滆礋杞絚puset */ + for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { + if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->ginfo.wd.cgid == cls && + strcmp(cgutil_vaddr[i]->grpname, topwd) != 0) { + if (cgexec_check_cpuset_value(cpuset, cgutil_vaddr[i]->cpuset) < 0) { + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[i], cgutil_vaddr[cls]->cpuset) == -1) + return -1; + } + } + } + + return 0; +} + +/** + * @Description: 鏇存柊鎵鏈夊伐浣滆礋杞界粍鐨刢puset鍊笺 + * @IN grp: 缁勪俊鎭 + * @IN cpuset: cpuset鍊 + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ +static int cgexec_update_all_workload_cgroup_cpuset(int cls, char* cpuset) +{ + int i; + char topwd[GPNAME_LEN]; + + /* 鑾峰彇'topwd'鐨勫畬鏁村悕绉 */ + errno_t rc = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); + securec_check_intval(rc, , -1); + + /* 璁剧疆鎵鏈夊伐浣滆礋杞絚puset */ + for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { + if (cgutil_vaddr[i]->used && cgutil_vaddr[i]->ginfo.wd.cgid == cls && + strcmp(cgutil_vaddr[i]->grpname, topwd) != 0) { + /* 濡傛灉涓婄骇缁勭殑cpuset鍊煎彉澶э紝宸ヤ綔璐熻浇缁勭殑鍊间篃浼氬彉澶 */ + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[i], cpuset) == -1) + return -1; + } + } + + return 0; +} +/* + * @Description: 鏇存柊鍓╀綑缁勭殑cpuset鍊笺 + * @IN cls: 绫荤粍id + * @IN level: 鍓╀綑缁勫眰绾d + * @IN cpuset: cpuset鍊 + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ +static int cgexec_update_remain_cgroup_cpuset_value(int cls, int level, char* cpuset) +{ + char* relpath = NULL; // 澹版槑涓涓寚鍚慶har绫诲瀷鐨勬寚閽堝彉閲弐elpath锛屽苟鍒濆鍖栦负NULL + char rempath[16]; // 澹版槑涓涓ぇ灏忎负16鐨勫瓧绗︽暟缁剅empath + int j; // 澹版槑涓涓暣鍨嬪彉閲廽 + errno_t sret; // 澹版槑涓涓猠rrno_t绫诲瀷鐨勫彉閲弒ret锛岀敤浜庡鐞嗛敊璇爜 + + for (j = WDCG_START_ID; j <= WDCG_END_ID; ++j) { // 寰幆浠嶹DCG_START_ID鍒癢DCG_END_ID + // 鍒ゆ柇cgutil_vaddr[j]鏄惁琚娇鐢ㄤ笖鍏秅info.wd.cgid绛変簬cls涓攇info.wd.wdlevel绛変簬level + if (cgutil_vaddr[j]->used && cgutil_vaddr[j]->ginfo.wd.cgid == cls && + cgutil_vaddr[j]->ginfo.wd.wdlevel == level) + break; // 濡傛灉鏉′欢婊¤冻锛屽垯璺冲嚭寰幆 + } + + /* 鑾峰彇宸ヤ綔璐熻浇缁勭殑鐖惰矾寰 */ + relpath = gscgroup_get_parent_wdcg_path(j, cgutil_vaddr, current_nodegroup); // 璋冪敤鍑芥暟gscgroup_get_parent_wdcg_path鑾峰彇宸ヤ綔璐熻浇缁勭殑鐖惰矾寰 + if (NULL == relpath) // 濡傛灉relpath涓虹┖锛岃繑鍥-1 + return -1; + + /* 鑾峰彇鍓╀綑缁勮矾寰 */ + sret = snprintf_s(rempath, // 鏍煎紡鍖栧瓧绗︿覆骞跺皢缁撴灉瀛樺偍鍦╮empath涓 + sizeof(rempath), + sizeof(rempath) - 1, + "%s:%d/", + GSCGROUP_REMAIN_WORKLOAD, + cgutil_vaddr[j]->ginfo.wd.wdlevel); + securec_check_intval(sret, free(relpath), -1); // 妫鏌ret鏄惁涓-1锛岃嫢鏄垯閲婃斁relpath骞惰繑鍥-1 + + sret = strcat_s(relpath, GPNAME_PATH_LEN, rempath); // 灏唕empath鎷兼帴鍒皉elpath涓 + securec_check_errno(sret, free(relpath), -1); // 妫鏌ret鏄惁涓-1锛岃嫢鏄垯閲婃斁relpath骞惰繑鍥-1 + + /* 浣跨敤鐩稿璺緞鏇存柊cgroup cpuset */ + if (cgexec_update_cgroup_cpuset_value(relpath, cpuset) == -1) { // 璋冪敤鍑芥暟cgexec_update_cgroup_cpuset_value鏇存柊cgroup cpuset锛岃嫢杩斿洖-1锛屽垯杈撳嚭閿欒淇℃伅骞惰繑鍥-1 + fprintf(stderr, + "ERROR: failed to add %s controller in %s:%d!\n", + MOUNT_CPUSET_NAME, + GSCGROUP_REMAIN_WORKLOAD, + cgutil_vaddr[j]->ginfo.wd.wdlevel); + free(relpath); // 閲婃斁relpath鐨勫唴瀛樼┖闂 + relpath = NULL; + return -1; + } + + free(relpath); // 閲婃斁relpath鐨勫唴瀛樼┖闂 + relpath = NULL; + + return 0; // 杩斿洖0琛ㄧず姝e父 +} + +/* + * @Description: 鏇存柊鍓╀綑缁勭殑cpuset銆 + * @IN cls: 绫荤粍id + * @IN cpuset: cpuset鍊 + * @IN update: 鏇存柊鏍囧織 + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ +static int cgexec_update_remain_cgroup_cpuset(int cls, char* cpuset, unsigned char update) +{ + int i; // 澹版槑涓涓暣鍨嬪彉閲廼 + + if (update) { + /* 浠庨珮绾у埆鍒颁綆绾у埆鏇存柊'remain'缁勭殑cpuset鍊 */ + for (i = cgutil_vaddr[cls]->ginfo.cls.maxlevel; i >= 1; --i) { // 寰幆浠庢渶澶х骇鍒埌1 + if (cgexec_update_remain_cgroup_cpuset_value(cls, i, cpuset) == -1) { // 璋冪敤鍑芥暟cgexec_update_remain_cgroup_cpuset_value鏇存柊鍓╀綑缁勭殑cpuset鍊硷紝鑻ヨ繑鍥-1锛屽垯杈撳嚭閿欒淇℃伅骞惰繑鍥-1 + fprintf(stderr, + "ERROR: failed to add %s controller in %s:%d, update: %d, cpuset: %s!\n", + MOUNT_CPUSET_NAME, + GSCGROUP_REMAIN_WORKLOAD, + i, + update, + cpuset); + return -1; + } + } + } + else { + /* 浠庝綆绾у埆鍒伴珮绾у埆鏇存柊'remain'缁勭殑cpuset鍊 */ + for (i = 1; i <= cgutil_vaddr[cls]->ginfo.cls.maxlevel; ++i) { // 寰幆浠1鍒版渶澶х骇鍒 + if (cgexec_update_remain_cgroup_cpuset_value(cls, i, cpuset) == -1) { // 璋冪敤鍑芥暟cgexec_update_remain_cgroup_cpuset_value鏇存柊鍓╀綑缁勭殑cpuset鍊硷紝鑻ヨ繑鍥-1锛屽垯杈撳嚭閿欒淇℃伅骞惰繑鍥-1 + fprintf(stderr, + "ERROR: failed to add %s controller in %s:%d, update: %d, cpuset: %s!\n", + MOUNT_CPUSET_NAME, + GSCGROUP_REMAIN_WORKLOAD, + i, + update, + cpuset); + return -1; + } + } + } + + return 0; // 杩斿洖0琛ㄧず姝e父 +} +/** + * @Description: 鏇存柊'class'缁勭殑cpuset鍊笺 + * @IN cls: class缁勭殑id + * @IN cpuset: cpuset鐨勫 + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ +static int cgexec_update_class_cpuset(int cls, char* cpuset) +{ + char largeset[CPUSET_LEN]; // 瀹氫箟涓涓猚puset鏁扮粍 + + (void)cgexec_check_workload_cgroup_cpuset(cls, cpuset); // 璋冪敤cgexec_check_workload_cgroup_cpuset鍑芥暟 + + /* 浣跨敤鏃х殑cpuset鍊煎拰鏂扮殑cpuset鍊艰幏鍙栧ぇ鑼冨洿 */ + cgexec_get_large_cupset(cgutil_vaddr[cls]->cpuset, cpuset, largeset); // 璋冪敤cgexec_get_large_cupset鍑芥暟锛屽皢缁撴灉璧嬪肩粰largeset鏁扮粍 + + /* + * 濡傛灉瑕佽缃竴涓粍鐨勬柊cpuset鍊硷紝蹇呴』纭繚涓婂眰缁勫叿鏈夊ぇ鑼冨洿锛 + * 鎴戜滑蹇呴』鍏堟洿鏂板叿鏈夊ぇ鑼冨洿璁剧疆鐨勭粍锛 + * 椤哄簭: class -> remain -> timeshare + * 鐒跺悗锛屾垜浠彲浠ヤ换鎰忔洿鏂扮粍鐨刢puset鍊笺 + */ + if (strcmp(cgutil_vaddr[cls]->cpuset, largeset) != 0 && // 鍒ゆ柇涓や釜cpuset鏁扮粍鏄惁鐩哥瓑 + (cgexec_update_cgroup_cpuset(cgutil_vaddr[cls], largeset) == -1 || // 璋冪敤cgexec_update_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_remain_cgroup_cpuset(cls, largeset, 0) == -1 || // 璋冪敤cgexec_update_remain_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_timeshare_cpuset(cgutil_vaddr[cls], largeset, 0) == -1 || // 璋冪敤cgexec_update_timeshare_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_topwd_cgroup_cpuset(cls, cpuset) == -1)) { // 璋冪敤cgexec_update_topwd_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + fprintf(stderr, "ERROR: failed to update cpuset for group in %s!\n", cgutil_vaddr[cls]->grpname); + return -1; + } + + /* + * 鎴戜滑浣跨敤鏂板艰缃墍鏈夊伐浣滆礋杞界粍鐨刢puset鍊硷紝杩欐牱 + * 鏇存柊瀹冧滑鐨勫兼槸瀹夊叏鐨勶紝鍥犱负涓婂眰缁勫凡缁忓叿鏈夊ぇ鑼冨洿 + * 璁剧疆鍊硷紝鐜板湪鎴戜滑鍙互鏇存柊杩欎簺涓婂眰缁勶紝 + * 椤哄簭: timeshare -> remain -> class + */ + if (cgexec_update_all_workload_cgroup_cpuset(cls, cpuset) == -1 || // 璋冪敤cgexec_update_all_workload_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_timeshare_cpuset(cgutil_vaddr[cls], cpuset, 1) == -1 || // 璋冪敤cgexec_update_timeshare_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_remain_cgroup_cpuset(cls, cpuset, 1) == -1 || // 璋冪敤cgexec_update_remain_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_topwd_cgroup_cpuset(cls, cpuset) == -1 || // 璋冪敤cgexec_update_topwd_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + cgexec_update_cgroup_cpuset(cgutil_vaddr[cls], cpuset) == -1) { // 璋冪敤cgexec_update_cgroup_cpuset鍑芥暟锛屽苟鍒ゆ柇鏄惁鎴愬姛 + fprintf(stderr, "ERROR: failed to update cpuset for timeshare group in %s!\n", cgutil_vaddr[cls]->grpname); + return -1; + } + + return 0; +} + +/** + * @Description: 鏇存柊鎵鏈塩lass缁勭殑cpuset鍊笺 + * @IN grp: 缁勪俊鎭 + * @IN cpuset: cpuset鐨勫 + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ +static int cgexec_update_all_class_cgroup_cpuset(char* cpuset) +{ + /* 灏嗘墍鏈塩lass缁勭殑cpuset鍊间粠榛樿鍊兼洿鏂颁负鏂扮殑cpuset鍊 */ + for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; ++i) { + if (cgutil_vaddr[i]->used == 0) + continue; + + if (cgexec_update_class_cpuset(i, cpuset) == -1) + return -1; + } + + return 0; +} + +/** + * @Description: 鏇存柊鎵鏈夊悗绔粍鐨刢puset鍊笺 + * @IN grp: 缁勪俊鎭 + * @IN cpuset: cpuset鐨勫 + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ +static int cgexec_update_all_backend_cgroup_cpuset(char* cpuset) +{ + /* 灏嗘墍鏈夊悗绔粍鐨刢puset鍊间粠榛樿鍊兼洿鏂颁负鏂扮殑cpuset鍊 */ + for (int i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; ++i) { + if (cgutil_vaddr[i]->used == 0) + continue; + + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[i], cpuset) == -1) + return -1; + } + + return 0; +} +/** + * @Description: 鏇存柊椤剁骇缁勭殑cpuset锛屽寘鎷細GAUSSDB锛孊ACKEND锛孋LASS銆 + * @IN top: 椤剁骇缁勭殑ID + * @IN cpuset: cpuset鐨勫 + * @Return: -1锛氬紓甯革紝0锛氭甯 + * @See also: + */ + +static int cgexec_update_top_group_cpuset(int top, char* cpuset) { + // 濡傛灉椤剁骇缁勬槸GAUSSDB + if (top == TOPCG_GAUSSDB) { + // 瀹氫箟largeset鏁扮粍 + char largeset[CPUSET_LEN]; + + // 瀹氫箟dir銆乨e + DIR* dir = NULL; + struct dirent* de = NULL; + + // 瀹氫箟path銆乻ubpath鍜宻tatbuf + char path[MAXPGPATH] = { 0 }; + char subpath[MAXPGPATH] = { 0 }; + struct stat statbuf; + errno_t rc; + int ret = -1; + bool ummap_flag = false; + + // 灏唖tatbuf鐨勫唴瀛樻竻闆 + rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); + securec_check_errno(rc, , -1); + + // 鏇存柊榛樿閰嶇疆鏂囦欢 + cgexec_get_large_cupset(cgutil_vaddr[TOPCG_GAUSSDB]->cpuset, cpuset, largeset); + + // 濡傛灉鏇存柊GAUSSDB鐨刢group cpuset澶辫触锛屾垨鑰呮洿鏂癇ACKEND鐨刢group cpuset澶辫触锛屾垨鑰呮洿鏂癈LASS鐨刢group cpuset澶辫触锛屽垯杩斿洖寮傚父 + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[TOPCG_GAUSSDB], largeset) == -1 || + cgexec_update_top_group_cpuset(TOPCG_BACKEND, cpuset) == -1 || + cgexec_update_top_group_cpuset(TOPCG_CLASS, cpuset) == -1) { + fprintf(stdout, "ERROR: update all cgroup cpuset failed.\n"); + return -1; + } + + // 鏍煎紡鍖杙ath + rc = snprintf_s(path, + sizeof(path), + sizeof(path) - 1, + "%s/%s:%s", + cgutil_opt.mpoints[0], + GSCGROUP_TOP_DATABASE, + cgutil_passwd_user->pw_name); + securec_check_intval(rc, , -1); + + // 濡傛灉鎵撳紑鐩綍澶辫触锛屽垯杩斿洖寮傚父 + if (NULL == (dir = opendir(path))) + return -1; + + // 閬嶅巻鐩綍 + while (NULL != (de = readdir(dir))) { + // 濡傛灉鏄綋鍓嶇洰褰曟垨涓婁竴绾х洰褰曞垯缁х画閬嶅巻 + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + continue; + + // 鏍煎紡鍖杝ubpath + rc = snprintf_s(subpath, sizeof(subpath), sizeof(subpath) - 1, "%s/%s", path, de->d_name); + securec_check_intval(rc, (void)closedir(dir); , -1); + + // 妫鏌ユ槸鍚︿负鐩綍 + ret = stat(subpath, &statbuf); + if (0 != ret || !S_ISDIR(statbuf.st_mode)) + continue; + + // 濡傛灉鐩綍涓寘鍚獹SCGROUP_TOP_BACKEND鍒欑户缁亶鍘 + if (NULL != strstr(de->d_name, GSCGROUP_TOP_BACKEND)) + continue; + + // 濡傛灉鐩綍涓寘鍚獹SCGROUP_TOP_CLASS鍒欑户缁亶鍘 + if (NULL != strstr(de->d_name, GSCGROUP_TOP_CLASS)) + continue; + + // 濡傛灉cgutil_vaddr[0]涓嶄负绌猴紝鍒欒В闄ゆ槧灏勶紝骞跺皢ummap_flag璁剧疆涓簍rue + if (cgutil_vaddr[0] != NULL) { + (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); + ummap_flag = true; + } + + // 鏍煎紡鍖朿gutil_opt.nodegroup + rc = snprintf_s( + cgutil_opt.nodegroup, sizeof(cgutil_opt.nodegroup), sizeof(cgutil_opt.nodegroup) - 1, "%s", de->d_name); + securec_check_intval(rc, (void)closedir(dir); , -1); + + current_nodegroup = cgutil_opt.nodegroup; + + // 瑙f瀽閫昏緫闆嗙兢鐨勯厤缃枃浠 + if (-1 == cgconf_parse_nodegroup_config_file()) { + (void)closedir(dir); + return -1; + } + + // 鏇存柊閫昏緫闆嗙兢鐨刢puset + } + ... + } + ... +} +*/ + +/** + * CGROUP锛圕ontrol Group锛夋槸Linux鍐呮牳鎻愪緵鐨勪竴绉嶆満鍒讹紝鐢ㄤ簬闄愬埗銆佽褰曞拰闅旂涓缁勮繘绋嬬殑璧勬簮锛堝CPU銆佸唴瀛樸佺鐩樼瓑锛変娇鐢ㄦ儏鍐点 + * 鏈唬鐮侀氳繃cgexec_update_top_group_cpuset鍑芥暟鏉ユ洿鏂伴《绾х粍鐨刢puset銆 + * + * 鍑芥暟涓寘鍚殑鍙橀噺鍙婂叾鍔熻兘锛 + * - top: 椤剁骇缁勭殑ID + * - cpuset: cpuset鐨勫 + * - largeset: 鐢ㄤ簬瀛樺偍鏇存柊鍚庣殑cpuset鍊 + * - dir, de: 鐢ㄤ簬閬嶅巻鐩綍 + * - path, subpath: 瀛樺偍璺緞 + * - statbuf: 瀛樺偍鐩綍鐨勬枃浠朵俊鎭 + * - ret: 瀛樺偍鏂囦欢淇℃伅鑾峰彇缁撴灉 + * - ummap_flag: 鏍囪瘑鏄惁瑙i櫎鏄犲皠 + * - rc: 瀛樺偍鍑芥暟杩斿洖鍊 + * + * 鍑芥暟鐨勭浉浼煎簲鐢ㄥ疄渚嬶細 + * - 涓涓被浼肩殑搴旂敤鍦烘櫙鏄郴缁熻祫婧愮殑鍒嗛厤鍜岄檺鍒躲備緥濡傦紝涓涓搷浣滅郴缁熷彲浠ュ皢涓缁勮繘绋嬪垎鍒颁竴涓猚group涓紝骞跺璇group涓殑杩涚▼杩涜璧勬簮闄愬埗锛屽CPU浣跨敤閲忋佸唴瀛樹娇鐢ㄩ噺绛夈 + */ + + /* + * 鍑芥暟鍚嶇О锛歝gexec_update_dynamic_class_cgroup + * 鎻忚堪锛氬綋绫籧group鎴栧伐浣滆礋杞絚group鐨勫姩鎬佸兼洿鏂版椂锛屾洿鏂扮浉搴旂殑绫婚厤缃拰宸ヤ綔璐熻浇閰嶇疆銆 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + static int cgexec_update_dynamic_class_cgroup(void) +{ + int i, cls = 0, wd = 0; + int percent = 0; + + /* 妫鏌ョ被鏄惁瀛樺湪 */ + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0) + continue; + + if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { + cls = i; + break; + } + } + + if (cls) { + if (cgutil_opt.clspct && (cgutil_opt.clspct > cgutil_vaddr[cls]->ginfo.cls.percent)) { + /* 妫鏌ュ墿浣欑殑鐧惧垎姣 */ + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + if (cgutil_vaddr[i]->used && (i != cls)) + percent += cgutil_vaddr[i]->ginfo.cls.percent; + } + + if ((GROUP_ALL_PERCENT - percent) < cgutil_opt.clspct) { + fprintf(stderr, + "閿欒锛氭病鏈夎冻澶熺殑璧勬簮鏉ユ洿鏂癱group %s銆俓n" + "鍓╀綑鐨勭櫨鍒嗘瘮涓 %d銆俓n", + cgutil_opt.clsname, + GROUP_ALL_PERCENT - percent); + return -1; + } + } + + if (cgutil_opt.clspct && cgutil_opt.clspct != cgutil_vaddr[cls]->ginfo.cls.percent) { + /* 鏇存柊cgutil_vaddr椤 */ + cgconf_update_class_group(cgutil_vaddr[cls]); + + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[cls])) + return -1; + } + + if (cgutil_opt.wdname[0]) { + wd = cgexec_search_workload_group(cls); + + if (wd && cgutil_opt.grppct) { + if (cgutil_opt.grppct >= (cgutil_vaddr[cls]->ginfo.cls.rempct + cgutil_vaddr[wd]->ginfo.wd.percent)) { + fprintf(stderr, + "閿欒锛氭病鏈夎冻澶熺殑璧勬簮鏉ユ洿鏂癱group %s銆俓n" + "鍓╀綑鐨勭櫨鍒嗘瘮涓 %d銆俓n", + cgutil_opt.wdname, + cgutil_vaddr[cls]->ginfo.cls.rempct + cgutil_vaddr[wd]->ginfo.wd.percent); + return -1; + } + + if (cgutil_opt.grppct != cgutil_vaddr[wd]->ginfo.wd.percent) { + cgconf_update_workload_group(cgutil_vaddr[wd]); + + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[wd])) + return -1; + + if (-1 == cgexec_update_remain_cgroup(cgutil_vaddr[wd], cls)) + return -1; + } + } + else if (wd == 0) { + fprintf(stderr, "閿欒锛氭寚瀹氱殑宸ヤ綔璐熻浇缁 %s 涓嶅瓨鍦紒\n", cgutil_opt.wdname); + return -1; + } + } + } + else { + fprintf(stderr, "閿欒锛氭寚瀹氱殑绫荤粍 %s 涓嶅瓨鍦紒\n", cgutil_opt.clsname); + return -1; + } + + return 0; +} + /* + * @Description: 妫鏌ュ姩鎬佸悗绔粍鐨勭櫨鍒嗘瘮鏄惁鍚堟硶銆 + * @IN bkd: 鍚庣缁刬d + * @Return: -1锛氬紓甯 0锛氭甯 + * @See also: + */ + static int cgexec_check_dynamic_backend_percent(int bkd) + { + int i = 0; + int percent = 0; + + if (cgutil_opt.bkdpct > 0 && cgutil_opt.bkdpct > cgutil_vaddr[bkd]->ginfo.cls.percent) { + // 妫鏌ュ墿浣欑櫨鍒嗘瘮 + for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { + if (cgutil_vaddr[i]->used && (i != bkd)) + percent += cgutil_vaddr[i]->ginfo.cls.percent; + } + + if ((GROUP_ALL_PERCENT - percent) < cgutil_opt.bkdpct) { + fprintf(stderr, + "ERROR: 娌℃湁瓒冲鐨勮祫婧愭潵鏇存柊鎺у埗缁%s銆俓n" + "鍓╀綑鐧惧垎姣斾负%d銆俓n", + cgutil_opt.bkdname, + GROUP_ALL_PERCENT - percent); + return -1; + } + } + + if (cgutil_opt.bkdpct > 0 && cgutil_opt.bkdpct != cgutil_vaddr[bkd]->ginfo.cls.percent) { + // 璁剧疆cgutil_vaddr椤圭洰 + cgconf_update_backend_group(cgutil_vaddr[bkd]); + + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[bkd])) + return -1; + } + + return 0; + } + + /* + * function name: cgexec_update_dynamic_backend_cgroup + * description : 鏇存柊鍚庣缁勭殑鍔ㄦ佸 + * return value : + * -1锛氬紓甯 + * 0锛氭甯 + */ + static int cgexec_update_dynamic_backend_cgroup(void) + { + int i, bkd = 0; + + // 妫鏌ョ被鏄惁瀛樺湪 + for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0) + continue; + + if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.bkdname)) { + bkd = i; + break; + } + } + + if (bkd) { + if (cgexec_check_dynamic_backend_percent(bkd) == -1) + return -1; + } + else { + fprintf(stderr, "ERROR: 鎸囧畾鐨勫悗绔粍%s涓嶅瓨鍦紒\n", cgutil_opt.bkdname); + return -1; + } + + return 0; + } + /* + * 鍑芥暟鍚: cgexec_update_top_group_percent + * 鎻忚堪锛氭洿鏂伴《绾group鐨勫姩鎬佸硷紱鍖呮嫭Root Cgroup銆丟uassdb:user Cgroup銆丆lass Cgroup + * 鍜孊ackend Cgroup銆 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + static int cgexec_update_top_group_percent(void) + { + // 濡傛灉椤剁骇Cgroup涓篟oot Cgroup + if (0 == strcmp(cgutil_opt.topname, GSCGROUP_ROOT)) { + // 闈濺oot鐢ㄦ埛涓嶈兘淇敼Root cgroup + if (geteuid() != 0) { + fprintf(stderr, "ERROR: 闈濺oot鐢ㄦ埛涓嶈兘淇敼Root cgroup锛乗n"); + return -1; + } + + // 濡傛灉鎸囧畾鐨勯《绾group鐨勭櫨鍒嗘瘮灏忎簬10锛屽皢鍏惰缃负10 + if (cgutil_opt.toppct < 10) { + cgutil_opt.toppct = 10; + } + + // 濡傛灉椤剁骇Cgroup鐨勭櫨鍒嗘瘮宸茬粡鏄寚瀹氱殑鍊硷紝鐩存帴杩斿洖 + if (cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent == cgutil_opt.toppct) + return 0; + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = cgutil_opt.toppct; + + // 鏇存柊椤剁骇Cgroup鐨処O鏉冮噸 + cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = MAX_IO_WEIGHT * cgutil_opt.toppct / GROUP_ALL_PERCENT; + + // 濡傛灉褰撳墠绯荤粺鏄疭LES11 SP2鐗堟湰锛屽苟涓攃gexec_check_SLESSP2_version鍑芥暟杩斿洖-1 + // 鍒欐洿鏂癈group鐨勫硷紝鍚﹀垯杩斿洖寮傚父 + if ((cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) && + (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_ROOT]))) + return -1; + } + // 濡傛灉椤剁骇Cgroup涓篏uassdb:user Cgroup + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE) || + 0 == strcmp(cgutil_opt.topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname)) { + + // 濡傛灉椤剁骇Cgroup鐨勭櫨鍒嗘瘮宸茬粡鏄寚瀹氱殑鍊硷紝鐩存帴杩斿洖 + if (cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent == cgutil_opt.toppct) + return 0; + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_GAUSSDB]->ginfo.top.percent = cgutil_opt.toppct; + + // 鏇存柊椤剁骇Cgroup鐨凜PU浠介 + cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.shares = + DEFAULT_CPU_SHARES * cgutil_opt.toppct / (GROUP_ALL_PERCENT - cgutil_opt.toppct); + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_GAUSSDB]->percent = + cgutil_vaddr[TOPCG_ROOT]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; + + // 濡傛灉鏇存柊Cgroup鐨勫艰繑鍥-1锛屽垯杩斿洖寮傚父 + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_GAUSSDB])) + return -1; + + // 鏇存柊椤剁骇Cgroup鐨勭浉鍏抽厤缃 + cgconf_update_top_percent(); + } + // 濡傛灉椤剁骇Cgroup涓築ackend Cgroup + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_BACKEND)) { + // 濡傛灉椤剁骇Cgroup鐨勭櫨鍒嗘瘮宸茬粡鏄寚瀹氱殑鍊硷紝鐩存帴杩斿洖 + if (cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent == cgutil_opt.toppct) + return 0; + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = cgutil_opt.toppct; + + // 鏇存柊椤剁骇Cgroup鐨凜PU浠介 + cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_opt.toppct / 10; + + // 鏇存柊椤剁骇Cgroup鐨処O鏉冮噸 + cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_opt.toppct); + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_BACKEND]->percent = + cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; + + // 濡傛灉鏇存柊Cgroup鐨勫艰繑鍥-1锛屽垯杩斿洖寮傚父 + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_BACKEND])) + return -1; + + // 鏇存柊Backend Cgroup鐨勭浉鍏抽厤缃 + cgconf_update_backend_percent(); + + // 鏇存柊Class Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = GROUP_ALL_PERCENT - cgutil_opt.toppct; + + // 鏇存柊Class Cgroup鐨凜PU浠介 + cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / 10; + + // 鏇存柊Class Cgroup鐨処O鏉冮噸 + cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, GROUP_ALL_PERCENT - cgutil_opt.toppct); + + // 鏇存柊Class Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_CLASS]->percent = + cgutil_vaddr[TOPCG_GAUSSDB]->percent * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / GROUP_ALL_PERCENT; + + // 濡傛灉鏇存柊Cgroup鐨勫艰繑鍥-1锛屽垯杩斿洖寮傚父 + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_CLASS])) + return -1; + + // 鏇存柊Class Cgroup鐨勭浉鍏抽厤缃 + cgconf_update_class_percent(); + } + // 濡傛灉椤剁骇Cgroup涓篊lass Cgroup + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_CLASS)) { + // 濡傛灉椤剁骇Cgroup鐨勭櫨鍒嗘瘮宸茬粡鏄寚瀹氱殑鍊硷紝鐩存帴杩斿洖 + if (cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent == cgutil_opt.toppct) + return 0; + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = cgutil_opt.toppct; + + // 鏇存柊椤剁骇Cgroup鐨凜PU浠介 + cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * cgutil_opt.toppct / 10; + + // 鏇存柊椤剁骇Cgroup鐨処O鏉冮噸 + cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, cgutil_opt.toppct); + + // 鏇存柊椤剁骇Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_CLASS]->percent = + cgutil_vaddr[TOPCG_GAUSSDB]->percent * cgutil_opt.toppct / GROUP_ALL_PERCENT; + + // 濡傛灉鏇存柊Cgroup鐨勫艰繑鍥-1锛屽垯杩斿洖寮傚父 + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_CLASS])) + return -1; + + // 鏇存柊Class Cgroup鐨勭浉鍏抽厤缃 + cgconf_update_class_percent(); + + // 鏇存柊Backend Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = GROUP_ALL_PERCENT - cgutil_opt.toppct; + + // 鏇存柊Backend Cgroup鐨凜PU浠介 + cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / 10; + + // 鏇存柊Backend Cgroup鐨処O鏉冮噸 + cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = + IO_WEIGHT_CALC(MAX_IO_WEIGHT, GROUP_ALL_PERCENT - cgutil_opt.toppct); + + // 鏇存柊Backend Cgroup鐨勭櫨鍒嗘瘮 + cgutil_vaddr[TOPCG_BACKEND]->percent = + cgutil_vaddr[TOPCG_GAUSSDB]->percent * (GROUP_ALL_PERCENT - cgutil_opt.toppct) / GROUP_ALL_PERCENT; + + // 濡傛灉鏇存柊Cgroup鐨勫艰繑鍥-1锛屽垯杩斿洖寮傚父 + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[TOPCG_BACKEND])) + return -1; + + // 鏇存柊Backend Cgroup鐨勭浉鍏抽厤缃 + cgconf_update_backend_percent(); + } + // 濡傛灉鎸囧畾鐨勯《绾group涓嶅瓨鍦 + else { + fprintf(stderr, "ERROR: 鎸囧畾鐨勯《绾group %s 涓嶅瓨鍦紒\n", cgutil_opt.topname); + return -1; + } + + return 0; + } + /* + * 鍑芥暟鍚嶏細cgexec_update_top_group_cpuset_userset + * 鍔熻兘锛氭牴鎹敤鎴疯缃殑"-f"鏇存柊椤跺眰cpuset + * @IN topname锛氶渶瑕佹洿鏂扮殑椤跺眰缁勫悕绉般 + * @IN cpuset锛氶渶瑕佹洿鏂扮殑鐢ㄦ埛璁剧疆cpuset銆 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + int cgexec_update_top_group_cpuset_userset(const char* topname, char* cpuset) + { + int topstart = 0; // 椤跺眰cpuset鐨勮捣濮嬪 + int topend = 0; // 椤跺眰cpuset鐨勭粨鏉熷 + int toplength = 0; // 椤跺眰cpuset鐨勯暱搴 + + int rcs = sscanf_s(cpuset, "%d-%d", &topstart, &topend); // 閫氳繃鏍煎紡鍖栧瓧绗︿覆灏哻puset瑙f瀽涓簊tart鍜宔nd涓や釜鍊 + if (rcs != 2) { // 濡傛灉瑙f瀽澶辫触 + fprintf(stderr, + "%s:%d failed on calling " + "security function.\n", + __FILE__, + __LINE__); + return -1; + } + + toplength = topend - topstart + 1; // 璁$畻椤跺眰cpuset鐨勯暱搴 + + /* 涓嶈兘鏇存敼鏍圭粍 */ + if (strcmp(topname, GSCGROUP_ROOT) == 0) { // 濡傛灉topname鏄牴缁 + fprintf(stdout, "ERROR: cpuset of Root can not be changed.\n"); + return -1; + } + if (strcmp(topname, GSCGROUP_TOP_DATABASE) == 0 || strcmp(topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname) == 0) { + /* + * 褰撴洿鏂伴《灞傜被鍒殑cpuset鏃讹紝 + * 闇瑕佹寜姣斾緥鏇存柊鎵鏈夊睘浜庤绫诲埆鐨勫瓙缁勩 + */ + if (cgexec_update_top_group_cpuset(TOPCG_GAUSSDB, cpuset) == -1 || + cgexec_reset_cpuset_cgroups(TOPCG_GAUSSDB, 0) == -1) + return -1; + /* + * 姣忔浣跨敤"-f"璁剧疆cpuset鏃讹紝 + * 闇瑕佸皢璇ョ粍鐨勯厤棰濋噸缃负0锛屼互闃叉涓嬫璁剧疆cpuset鏃跺彈鍒板奖鍝嶃 + */ + cgutil_vaddr[TOPCG_GAUSSDB]->ainfo.quota = 0; + } + + return 0; + } + /* + * 鍑芥暟鍚嶏細cgexec_update_dynamic_top_cgroup + * 鍔熻兘锛氭洿鏂伴《灞侰group鐨勫姩鎬佸硷紝鍖呮嫭Root Cgroup銆丟uassdb:user Cgroup銆丆lass Cgroup鍜孊ackend Cgroup銆 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + static int cgexec_update_dynamic_top_cgroup(void) + { + if (cgutil_opt.toppct > 0 && cgexec_update_top_group_percent() == -1) { // 濡傛灉鐢ㄦ埛璁剧疆浜唗oppct涓旀洿鏂伴《灞傜粍鐨勭櫨鍒嗘瘮澶辫触 + return -1; + } + + if (*cgutil_opt.sets) // 濡傛灉鐢ㄦ埛璁剧疆浜唖ets + return cgexec_update_top_group_cpuset_userset(cgutil_opt.topname, cgutil_opt.sets); + + return 0; + } + /* + * 鍑芥暟鍚嶏細cgexec_update_fixed_class_cgroup + * 鍔熻兘锛氭牴鎹敤鎴疯缃殑"--fixed"閫夐」锛屾洿鏂癈lass缁勫拰Workload缁勭殑cpuset鍊 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + static int cgexec_update_fixed_class_cgroup(void) + { + int i, cls = 0, wd = 0; + char cpusets[CPUSET_LEN]; // 淇濆瓨cpuset鍊肩殑瀛楃鏁扮粍 + int need_reset = 0; // 鏍囧織鍙橀噺锛岃〃绀烘槸鍚﹂渶瑕侀噸缃 + + /* 妫鏌lass缁勬槸鍚﹀瓨鍦 */ + for (i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0) + continue; + + if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.clsname)) { + cls = i; + break; + } + } + + if (cls) { + if (cgutil_opt.wdname[0]) { + wd = cgexec_search_workload_group(cls); + + if (wd) { + if (cgutil_opt.setspct) { + /* + * 姝ラ1锛氭鏌ユ柊璁剧疆鐨勭櫨鍒嗘瘮鏄惁浣挎荤櫨鍒嗘瘮瓒呰繃100%銆 + * 缁撴灉锛 + * setslength = -1锛氳秴杩100%銆 + * setslength = 0锛歝pusets宸茬粡璁剧疆濂戒簡锛岃繘鍏ユ楠3銆 + * setslength > 0锛歝pusets涓虹┖锛岄渶瑕侀噸缃紝杩涘叆姝ラ2銆 + */ + if ((need_reset = cgexec_check_cpuset_percent(cls, wd, cpusets)) == -1) + return -1; + + /* 姝ラ2锛氬鍏朵粬宸ヤ綔璐熻浇缁勮繘琛岀鐗囨暣鐞 */ + if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(cls, wd) == -1)) + return -1; + + /* 姝ラ3锛氭洿鏂板伐浣滆礋杞界粍 */ + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[wd], cpusets) == -1) + return -1; + + cgutil_vaddr[wd]->ainfo.quota = cgutil_opt.setspct; + + return 0; + } + else if (cgutil_opt.setfixed) { + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[wd], cgutil_vaddr[cls]->cpuset) == -1) + return -1; + + cgutil_vaddr[wd]->ainfo.quota = 0; + + return 0; + } + } + else { + fprintf(stderr, "ERROR: 鎸囧畾鐨勫伐浣滆礋杞界粍%s涓嶅瓨鍦紒\n", cgutil_opt.wdname); + return -1; + } + } + if (cgutil_opt.setspct) { + /* + * 姝ラ1锛氭鏌ョ被鐨勬柊璁剧疆鐧惧垎姣旀槸鍚﹀悎娉曘 + * 杩斿洖鍊 = -1锛氫笉鍚堟硶 + * 杩斿洖鍊 = 0锛氬悎娉曪紝涓嶉渶瑕佽繘琛岀鐗囨暣鐞 + * 杩斿洖鍊 > 0锛氶渶瑕佽繘琛岀鐗囨暣鐞 + */ + if ((need_reset = cgexec_check_cpuset_percent(TOPCG_CLASS, cls, cpusets)) == -1) + return -1; + + /* 姝ラ2锛氬鍏朵粬绫荤粍杩涜纰庣墖鏁寸悊锛屾寜鐧惧垎姣旈噸缃畠浠墍灞炵殑宸ヤ綔璐熻浇缁 */ + if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(TOPCG_CLASS, cls) == -1)) + return -1; + + /* 姝ラ3锛氭洿鏂扮被缁 */ + if (cgexec_update_class_cpuset(cls, cpusets) == -1) { + fprintf(stderr, "ERROR: 鏇存柊绫荤粍\"%s\"鐨刢puset澶辫触銆俓n", cgutil_vaddr[cls]->grpname); + return -1; + } + + /* + * 姝ラ4锛氭洿鏂版墍灞炵殑宸ヤ綔璐熻浇缁 + * 0 琛ㄧず鍦ㄩ噸缃垪琛ㄤ腑娌℃湁闇瑕佽蹇界暐鐨勭粍 + */ + if (cgexec_reset_cpuset_cgroups(cls, 0) == -1) { + fprintf(stderr, "ERROR: 閲嶇疆绫荤粍\"%s\"鐨勫伐浣滆礋杞絚puset澶辫触銆俓n", cgutil_vaddr[cls]->grpname); + return -1; + } + cgutil_vaddr[cls]->ainfo.quota = cgutil_opt.setspct; + + } + else if (cgutil_opt.setfixed) { + if (cgexec_update_class_cpuset(cls, cgutil_vaddr[TOPCG_CLASS]->cpuset) == -1) + return -1; + + if (cgexec_reset_cpuset_cgroups(cls, 0) == -1) { + fprintf(stderr, "ERROR: 閲嶇疆绫荤粍\"%s\"鐨勫伐浣滆礋杞絚puset澶辫触銆俓n", cgutil_vaddr[cls]->grpname); + return -1; + } + + cgutil_vaddr[cls]->ainfo.quota = 0; + } + } + else { + fprintf(stderr, "ERROR: 鎸囧畾鐨勭被缁%s涓嶅瓨鍦紒\n", cgutil_opt.clsname); + return -1; + } + + return 0; + } + /* 鍑芥暟鍚嶏細cgexec_update_fixed_backend_cgroup + * 鍔熻兘锛氭牴鎹敤鎴疯缃殑"--fixed"閫夐」鏇存柊Backend缁勭殑cpuset鍊 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + */ + static int cgexec_update_fixed_backend_cgroup(void) + { + int i, bkd = 0; // 鍙橀噺i锛宐kd琛ㄧずBackend缁勭殑绱㈠紩鍜屾爣璁 + char cpuset[CPUSET_LEN]; // 瀛楃鏁扮粍cpuset淇濆瓨cpuset鐨勫 + int need_reset = 0; // 鏍囪鏄惁闇瑕侀噸缃甤puset + + /* 妫鏌ヨ绫绘槸鍚﹀瓨鍦 */ + for (i = BACKENDCG_START_ID; i <= BACKENDCG_END_ID; i++) { + if (cgutil_vaddr[i]->used == 0) // 妫鏌ヨ缁勬槸鍚﹁浣跨敤 + continue; + + if (0 == strcmp(cgutil_vaddr[i]->grpname, cgutil_opt.bkdname)) { // 妫鏌ョ粍鍚嶆槸鍚﹀尮閰 + bkd = i; + break; + } + } + + if (bkd) { + /* 鎸夌収鐧惧垎姣旇缃甤puset */ + if (cgutil_opt.setspct) { + /* 涓庢洿鏂板伐浣滆礋杞界粍鐩稿悓鐨勬楠 */ + if ((need_reset = cgexec_check_cpuset_percent(TOPCG_BACKEND, bkd, cpuset)) == -1) // 妫鏌ユ槸鍚﹂渶瑕侀噸缃甤puset + return -1; + + if (need_reset > 0 && (cgexec_reset_cpuset_cgroups(TOPCG_BACKEND, bkd) == -1)) // 濡傛灉闇瑕侀噸缃紝鍒欓噸缃甤puset + return -1; + + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[bkd], cpuset) == -1) // 鏇存柊cpuset鍊 + return -1; + + cgutil_vaddr[bkd]->ainfo.quota = cgutil_opt.setspct; // 鏇存柊quota鍊 + } + else if (cgutil_opt.setfixed) { + if (cgexec_update_cgroup_cpuset(cgutil_vaddr[bkd], cgutil_vaddr[TOPCG_BACKEND]->cpuset) == -1) // 鏇存柊cpuset鍊 + return -1; + + cgutil_vaddr[bkd]->ainfo.quota = 0; // 灏唓uota鍊肩疆涓0 + } + } + else { + fprintf(stderr, "ERROR: the specified backend group %s doesn't exist!\n", cgutil_opt.bkdname); + return -1; // 缁勪笉瀛樺湪锛岃繑鍥炲紓甯 + } + + return 0; // 杩斿洖姝e父 + } + + /* 鍑芥暟鍚嶏細cgexec_update_fixed_top_cgroup + * 鍔熻兘锛氭牴鎹敤鎴疯缃殑"--fixed"閫夐」鏇存柊Top缁勭殑cpuset鍊 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + */ + static int cgexec_update_fixed_top_cgroup(void) + { + char cpusets[CPUSET_LEN]; // 瀛楃鏁扮粍cpusets淇濆瓨cpuset鐨勫 + int need_reset = 0; // 鏍囪鏄惁闇瑕侀噸缃甤puset + int top = 0; // 鏍囪Top缁勭殑绱㈠紩 + + if (0 == strcmp(cgutil_opt.topname, GSCGROUP_ROOT)) { // 濡傛灉鏄牴缁勶紝鍒欐棤娉曚慨鏀 + fprintf(stderr, "ERROR: users can't modify the Root cgroup with \"--fixed\"!\n"); + return -1; + } + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE) || + 0 == strcmp(cgutil_opt.topname, cgutil_vaddr[TOPCG_GAUSSDB]->grpname)) { // 濡傛灉鏄疓aussdb缁勶紝鍒欐棤娉曚慨鏀筩puset鐨勭櫨鍒嗘瘮 + top = TOPCG_GAUSSDB; + if (cgutil_opt.setspct) { + fprintf(stderr, "ERROR: users can't modify the cpu cores percentage of Gaussdb cgroup with \"--fixed\"!\n"); + return -1; + } + } + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_BACKEND)) + top = TOPCG_BACKEND; + else if (0 == strcmp(cgutil_opt.topname, GSCGROUP_TOP_CLASS)) + top = TOPCG_CLASS; + else { + fprintf(stderr, "ERROR: the specified top group %s doesn't exist!\n", cgutil_opt.topname); + return -1; // 鏈壘鍒版寚瀹氱殑缁勶紝杩斿洖寮傚父 + } + + if (top) { + if (cgutil_opt.setspct) { + if ((need_reset = cgexec_check_cpuset_percent(TOPCG_GAUSSDB, top, cpusets)) == -1) // 妫鏌ユ槸鍚﹂渶瑕侀噸缃甤puset + return -1; + + if (need_reset && (cgexec_reset_cpuset_cgroups(TOPCG_GAUSSDB, top) == -1)) // 濡傛灉闇瑕侀噸缃紝鍒欓噸缃甤puset + return -1; + + if (cgexec_update_top_group_cpuset(top, cpusets) == -1 || cgexec_reset_cpuset_cgroups(top, 0) == -1) // 鏇存柊cpuset鍊煎苟閲嶇疆cpuset + return -1; + + cgutil_vaddr[top]->ainfo.quota = cgutil_opt.setspct; // 鏇存柊quota鍊 + } + else if (cgutil_opt.setfixed) { + if (cgexec_update_top_group_cpuset(top, cgutil_vaddr[TOPCG_GAUSSDB]->cpuset) == -1 || + cgexec_reset_cpuset_cgroups(top, 0) == -1) // 鏇存柊cpuset鍊煎苟閲嶇疆cpuset + return -1; + + cgutil_vaddr[top]->ainfo.quota = 0; // 灏唓uota鍊肩疆涓0 + } + } + + return 0; // 杩斿洖姝e父 + } + /* + **************** EXTERNAL FUNCTION ******************************** + */ + + /* + * function name: cgexec_check_SLESSP2_version + * description : 妫鏌ュ綋鍓嶆搷浣滅郴缁熺増鏈槸鍚︿负SLES SP2 + * return value : + * 1: 鏄疭LES SP2 + * 0: 涓嶆槸SLES SP2锛屽亣瀹氫负SLES SP1 + * -1: 寮傚父 + * + * 娉ㄦ剰锛氬湪/proc/cgroups涓悳绱"io"鍒椼 + * 闇瑕佹鏌ュ兼槸鍚︽敮鎸佷笅涓涓増鏈殑Redhat鎴朎uler銆 + */ + int cgexec_check_SLESSP2_version(void) + { + char buf[PROCLINE_LEN]; + FILE* f = NULL; + + f = fopen("/proc/cgroups", "r"); + + if (f == NULL) + return -1; + + while (NULL != fgets(buf, PROCLINE_LEN, f)) { + /* proc涓殑绀轰緥锛 + * #subsys_name hierarchy num_cgroups enabled + * cpu 0 1 1 + * + * 鎼滅储"blkio"鍒 + */ + + if (strstr(buf, MOUNT_BLKIO_NAME) != NULL) { + cgutil_is_sles11_sp2 = 1; + fclose(f); + return 1; + } + } + + fclose(f); + return 0; + } + + /* + * @Description: 妫鏌ユ槸鍚︽墽琛屽崌绾с + * @IN void + * @Return: 1: 鍗囩骇 0: 涓嶅崌绾 + * @See also: + */ + int cgexec_check_mount_for_upgrade(void) + { + int i, ret, old_mp = 0; + + errno_t sret; + + /* 鍙湁root鐢ㄦ埛鍙互鎵ц鍗囩骇 */ + if (geteuid() != 0) + return 0; + + /* + * 濡傛灉cpuset鍜宑puacct鏈寕杞斤紝鎴戜滑灏嗘鏌pu鎴朾lkio鏄惁鎸傝浇鍦ㄩ粯璁ょ偣涓婏紝濡傛灉鏄紝鍒欏繀椤诲厛鍗歌浇瀹冧滑锛 + * 鐒跺悗浣跨敤鏂扮殑鎸傝浇鐐规寕杞芥墍鏈夊瓙绯荤粺銆傚鏋滃湪榛樿鐐逛笂娌℃湁绯荤粺锛屽垯涓嶉渶瑕佸嵏杞藉畠浠 + */ + for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + /* 蹇界暐blkio鍜宮emory */ + if (i == MOUNT_BLKIO_ID || i == MOUNT_MEMORY_ID) + continue; + + if (*cgutil_opt.mpoints[i]) { + if (strcmp(cgutil_opt.mpoints[i], GSCGROUP_MOUNT_POINT_OLD) == 0) { + char fname[256]; + int cnt = 0; + struct dirent* file = NULL; + DIR* dir = opendir(GSCGROUP_MOUNT_POINT_OLD); + + if (dir == NULL) { + fprintf(stderr, "ERROR: failed to open %s.\n", GSCGROUP_MOUNT_POINT_OLD); + return -1; + } + + sret = snprintf_s( + fname, sizeof(fname), sizeof(fname) - 1, "%s:%s", "Gaussdb", cgutil_passwd_user->pw_name); + securec_check_intval(sret, closedir(dir), -1); + + /* 濡傛灉鍏朵粬鐢ㄦ埛鍦ㄩ粯璁ゆ寕杞界偣涓垱寤轰簡cgroup锛屽垯鏃犳硶鍗歌浇璇ョ偣銆 */ + while ((file = readdir(dir)) != NULL) { + if (file->d_type != DT_DIR || strcmp(file->d_name, fname) == 0) + continue; + + if (file->d_type == DT_DIR && strncmp(file->d_name, "Gaussdb:", 8) == 0) + ++cnt; + } + + closedir(dir); + + if (cnt > 0) { + fprintf(stderr, + "ERROR: The other user has cgroups in \"%s\", upgrade failed.\n", + GSCGROUP_MOUNT_POINT_OLD); + return -1; + } + + old_mp++; + } + } + } + + /* 濡傛灉cgroups鎸傝浇鍦ㄦ棫璺緞涓婏紝鍙嵏杞戒竴娆 */ + if (old_mp) { + char cmd[128]; + + /* 鍦/dev/cgroups涓嬫寕杞戒簡澶氫釜cgroups */ + if (old_mp > 1) { + (void)cgroup_init(); /* 鍒濆鍖 */ + (void)cgptree_drop_cgroups(); + } + + sret = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", GSCGROUP_MOUNT_POINT_OLD); + securec_check_intval(sret, , -1); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", GSCGROUP_MOUNT_POINT_OLD); + return -1; + } + + /* 鑾峰彇鏂扮殑鎸傝浇鐐瑰苟鎸傝浇瀹冧滑 */ + (void)cgexec_get_mount_points(); + (void)cgexec_mount_root_cgroup(); + } + else + cgutil_opt.upgrade = 0; + + return 0; + } + /* + * @Description: 鑾峰彇鎵鏈塩group瀛愮郴缁熺殑鎸傝浇鐐广 + * @IN void + * @Return: 0: 姝e父 -1: 寮傚父 + * @See also: + */ + int cgexec_get_mount_points(void) + { + struct mntent* ent = NULL; + char mntent_buffer[5 * FILENAME_MAX]; // 缂撳啿鍖哄ぇ灏 + + struct mntent temp_ent; // 鐢ㄤ簬瑙f瀽鎸傝浇鐐圭殑涓存椂缁撴瀯浣 + int i; + + errno_t rc; // 閿欒鐮 + rc = memset_s(&temp_ent, sizeof(temp_ent), 0, sizeof(temp_ent)); // 鍒濆鍖栦复鏃剁粨鏋勪綋 + securec_check_errno(rc, , -1); + + /* reset mount points */ + rc = memset_s(cgutil_opt.mpoints, MOUNT_SUBSYS_KINDS * MAXPGPATH, 0, MOUNT_SUBSYS_KINDS * MAXPGPATH); // 閲嶇疆鎸傝浇鐐规暟缁 + securec_check_errno(rc, , -1); + + /* open '/proc/mounts' to load mount points */ + FILE* proc_mount = fopen("/proc/mounts", "re"); // 鎵撳紑/proc/mounts鏂囦欢 + + if (proc_mount == NULL) + return -1; + + while ((ent = getmntent_r(proc_mount, &temp_ent, mntent_buffer, sizeof(mntent_buffer))) != NULL) { // 浠/proc/mounts涓鍙栨寕杞界偣淇℃伅 + /* not cgroup, pass */ + if (strcmp(ent->mnt_type, "cgroup") != 0) // 濡傛灉涓嶆槸cgroup鎸傝浇鐐癸紝璺宠繃 + continue; + + for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + if (hasmntopt(ent, cgutil_subsys_table[i]) == NULL) // 濡傛灉鎸傝浇鐐逛笉鍖呭惈褰撳墠瀛愮郴缁燂紝璺宠繃 + continue; + + /* get mount point */ + rc = snprintf_s(cgutil_opt.mpoints[i], // 鑾峰彇鎸傝浇鐐硅矾寰 + sizeof(cgutil_opt.mpoints[i]), + sizeof(cgutil_opt.mpoints[i]) - 1, + "%s", + ent->mnt_dir); + securec_check_intval(rc, fclose(proc_mount), -1); + } + } + + fclose(proc_mount); // 鍏抽棴/proc/mounts鏂囦欢 + + return 0; + } + + /* + * @Description: 妫娴媍group鏂囦欢绯荤粺鏄惁宸茬粡鎸傝浇銆 + * @IN void + * @Return: 1: 宸茬粡鎸傝浇鍦ㄦ寚瀹氱洰褰 + * 0: 鏈寕杞 + * -1: 宸茬粡鎸傝浇鍦ㄥ叾浠栫洰褰 + * @See also: + */ + int cgexec_detect_cgroup_mount(void) + { + int i, j; + + if (cgutil_opt.cflag <= 0) { + return 1; + } + + for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + if (i == MOUNT_BLKIO_ID) + continue; + + /* a subsys has not mounted, we must make sure its mount point is valid. */ + if (*cgutil_opt.mpoints[i] == '\0') { // 濡傛灉鎸傝浇鐐逛负绌 + if (cgutil_opt.mpflag == 0) { // 濡傛灉娌℃湁鏂扮殑鎸傝浇鐐 + /* no new mount point, make sure default point is valid */ + for (j = 0; j < MOUNT_SUBSYS_KINDS; ++j) + if (strcmp(cgutil_opt.mpoints[j], GSCGROUP_MOUNT_POINT) == 0) // 榛樿鎸傝浇鐐逛负鏈夋晥鐨 + return -1; + } + + return 0; + } + } + + return 1; + } + // 瀹氫箟涓涓潤鎬佸嚱鏁帮紝鐢ㄤ簬鍒犻櫎宸插瓨鍦ㄧ殑绗﹀彿閾炬帴 + static int RemoveExistSymbolLink(const char* mpoint) + { + int ret; + char cmd[MAX_COMMAND_LENGTH]; + struct stat statbuf; + errno_t rc; + + // 鍒濆鍖杝tatbuf涓0 + rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); + securec_check_c(rc, "\0", "\0"); + + // 妫鏌ョ鍙烽摼鎺ユ槸鍚﹀瓨鍦 + ret = lstat(mpoint, &statbuf); + if (S_ISLNK(statbuf.st_mode)) { // 濡傛灉鏄鍙烽摼鎺 + // 鍒犻櫎绗﹀彿閾炬帴 + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "rm %s", mpoint); + securec_check_ss_c(rc, "\0", "\0"); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to remove exist symbol link %s!\n", mpoint); + return -1; + } + } + return 0; + } + + // 瀹氫箟涓涓潤鎬佸嚱鏁帮紝鐢ㄤ簬鎸傝浇cgroup鏂囦欢绯荤粺 + static int MountCgroupInternal(const char* mpoint, const char* type) + { + int ret; + char cmd[MAX_COMMAND_LENGTH]; + struct stat statbuf; + errno_t rc; + + // 鍒濆鍖杝tatbuf涓0 + rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); + securec_check_c(rc, "\0", "\0"); + + // 妫鏌ユ柊鐨勬寕杞界偣鐩綍鏄惁瀛樺湪 + ret = stat(mpoint, &statbuf); + if (ret != 0 || !S_ISDIR(statbuf.st_mode)) { // 濡傛灉鏂扮殑鎸傝浇鐐圭洰褰曚笉瀛樺湪 + // 鍒涘缓鏂扮殑鎸傝浇鐐圭洰褰 + if (mkdir(mpoint, S_IRWXU) != 0) { + fprintf(stderr, "ERROR: failed to create %s directory!\n", mpoint); + return -1; + } + } + + // 鎸傝浇cgroup鏂囦欢绯荤粺鍒版柊鐨勬寕杞界偣鐩綍 + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "mount -t cgroup -o %s %s %s", type, type, mpoint); + securec_check_ss_c(rc, "\0", "\0"); + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to mount cgroup under %s!\n", mpoint); + return -1; + } + fprintf(stderr, "LOG: mount %s success.\n", type); + return 0; + } + + // 瀹氫箟涓涓潤鎬佸嚱鏁帮紝鐢ㄤ簬鍒涘缓绗﹀彿閾炬帴 + static int LinkCpuCgroup(const char* target, const char* source) + { + int ret; + char cmd[MAX_COMMAND_LENGTH]; + errno_t rc; + + // 鍒涘缓绗﹀彿閾炬帴 + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "ln -s %s %s", source, target); + securec_check_ss_c(rc, "\0", "\0"); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to mount cgroup under %s!\n", target); + return -1; + } + + return 0; + } + + // 瀹氫箟涓涓潤鎬佸嚱鏁帮紝鐢ㄤ簬閲嶆柊鎸傝浇CPU cgroup + static int CgexecRemountCpuCgroup(const char* path, const char* tmp_mpoint) + { + int ret; + char mpoint[MOUNT_POINT_LENGTH]; + errno_t rc; + + // 鎷兼帴鏂扮殑鎸傝浇鐐硅矾寰 + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/cpu", path); + securec_check_ss_c(rc, "\0", "\0"); + + // 鍒犻櫎宸插瓨鍦ㄧ殑绗﹀彿閾炬帴 + ret = RemoveExistSymbolLink(mpoint); + if (ret != 0) { + return ret; + } + + // 鍒涘缓CPU cgroup鐨勭鍙烽摼鎺 + ret = LinkCpuCgroup(mpoint, tmp_mpoint); + if (ret != 0) { + return ret; + } + + // 鎷兼帴鏂扮殑鎸傝浇鐐硅矾寰 + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/cpuacct", path); + securec_check_ss_c(rc, "\0", "\0"); + + // 鍒犻櫎宸插瓨鍦ㄧ殑绗﹀彿閾炬帴 + ret = RemoveExistSymbolLink(mpoint); + if (ret != 0) { + return ret; + } + + // 鍒涘缓CPU cgroup鐨勭鍙烽摼鎺 + ret = LinkCpuCgroup(mpoint, tmp_mpoint); + + return ret; + } + + // 鍑芥暟鍔熻兘锛氬湪鎸囧畾璺緞涓婃寕杞絚pu鍜宑puacct瀛愮郴缁熺殑Cgroup鏂囦欢绯荤粺 + // 鍙傛暟锛歝onst char* path - 鎸囧畾璺緞 + // 杩斿洖鍊硷細int - 杩斿洖0琛ㄧず鎸傝浇鎴愬姛锛岃繑鍥炲叾浠栧艰〃绀烘寕杞藉け璐 + + static int CgexecMountCpuCgroup(const char* path) + { + int ret; + char tmp_mpoint[MOUNT_POINT_LENGTH]; // 涓存椂鎸傝浇璺緞 + char mpoint[MOUNT_POINT_LENGTH]; // 鎸傝浇璺緞 + errno_t rc; + + // 鎷兼帴鎸傝浇璺緞 + rc = snprintf_s(tmp_mpoint, sizeof(tmp_mpoint), sizeof(tmp_mpoint) - 1, + "%s/cpu,cpuacct", path); + securec_check_ss_c(rc, "\0", "\0"); + + // 鎸傝浇cpu鍜宑puacct瀛愮郴缁 + ret = MountCgroupInternal(tmp_mpoint, "cpu,cpuacct"); + if (ret != 0) { + // 鎸傝浇澶辫触锛岃〃绀篶pu鍜宑puacct娌℃湁涓璧锋寕杞芥垚鍔 + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, + "%s/cpu", path); + securec_check_ss_c(rc, "\0", "\0"); + ret = MountCgroupInternal(mpoint, cgutil_subsys_table[MOUNT_CPU_ID]); + if (ret != 0) { + return ret; + } + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, + "%s/cpuacct", path); + securec_check_ss_c(rc, "\0", "\0"); + ret = MountCgroupInternal(mpoint, cgutil_subsys_table[MOUNT_CPUACCT_ID]); + } + else { + // 鎸傝浇鎴愬姛锛岃皟鐢–gexecRemountCpuCgroup鍑芥暟閲嶆柊鎸傝浇cpu鍜宑puacct瀛愮郴缁 + ret = CgexecRemountCpuCgroup(path, tmp_mpoint); + } + + return ret; + } + // 鍑芥暟鍔熻兘锛氭寕杞芥牴鐩綍涓婄殑Cgroup鏂囦欢绯荤粺 + // 鍙傛暟锛歷oid + // 杩斿洖鍊硷細int - 杩斿洖0琛ㄧず鎸傝浇鎴愬姛锛岃繑鍥-1琛ㄧず鎸傝浇澶辫触 + + int cgexec_mount_root_cgroup(void) + { + int i, ret; + + char mpoint[MOUNT_POINT_LENGTH]; // 鎸傝浇璺緞 + char* path = NULL; // 鎸傝浇鐩綍 + struct stat statbuf; // 鏂囦欢鐘舵佷俊鎭粨鏋勪綋 + + errno_t rc; + rc = memset_s(&statbuf, sizeof(statbuf), 0, sizeof(statbuf)); + securec_check_c(rc, "\0", "\0"); + + // 璁剧疆鎸傝浇鐩綍 + if (cgutil_opt.mpflag) + path = cgutil_opt.mpoint; + else + path = GSCGROUP_MOUNT_POINT; + + // 妫鏌ュ悗绔幆澧冨彉閲忔槸鍚︽甯 + if (CheckBackendEnv(path) != 0) { + return -1; + } + + // 鍒涘缓鎸傝浇璺緞鐩綍 + ret = stat(path, &statbuf); + if (0 != ret || !S_ISDIR(statbuf.st_mode)) { + if (mkdir(path, S_IRWXU | (S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != 0) { + fprintf(stderr, "ERROR: failed to create %s directory!\n", path); + return -1; + } + (void)chmod(path, S_IRWXU | (S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); // 淇敼鏉冮檺涓755 + } + + // 鎸傝浇鍚勪釜瀛愮郴缁熺殑Cgroup鏂囦欢绯荤粺 + for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + // 'blkio'瀛愮郴缁熸棤鏁堬紝蹇界暐 + if (i == MOUNT_BLKIO_ID) + continue; + + // 濡傛灉瀛愮郴缁熸湭鎸傝浇锛屼娇鐢ㄦ柊鐨勮矾寰勮繘琛屾寕杞 + if (*cgutil_opt.mpoints[i] == '\0') { + // 鍦ㄦ柊鐨凩inux绯荤粺涓婏紝cpu鍜宑puacct瀛愮郴缁熼兘鎸傝浇鍦╟pu,cpuacct璺緞涓 + if (i == MOUNT_CPU_ID) { + ret = CgexecMountCpuCgroup(path); + i++; + } + else { + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/%s", path, cgutil_subsys_table[i]); + securec_check_ss_c(rc, "\0", "\0"); + ret = MountCgroupInternal(mpoint, cgutil_subsys_table[i]); + } + } + } + + return 0; + } + // 鍑芥暟鍔熻兘锛氬嵏杞紺group鏂囦欢绯荤粺 + // 鍑芥暟鍙傛暟锛歝onst char* path - Cgroup鏂囦欢绯荤粺鐨勮矾寰 + // int index - Cgroup瀛愮郴缁熺殑绱㈠紩 + // 鍑芥暟杩斿洖鍊硷細-1琛ㄧず寮傚父锛0琛ㄧず姝e父 + static int CgexecUmountRootCgroupInternal(const char* path, int index) + { + int ret; + char cmd[MAX_COMMAND_LENGTH], mpoint[MOUNT_POINT_LENGTH]; + errno_t rc; + + // 鑾峰彇鎸傝浇鐐圭殑瀹屾暣璺緞 + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/%s", path, cgutil_subsys_table[index]); + securec_check_ss_c(rc, "\0", "\0"); + + // 濡傛灉鎸囧畾鐨勬寕杞界偣鍜宑gutil_opt.mpoints[index]鐩哥瓑锛屽垯鍗歌浇鎸囧畾鐨勬寕杞界偣 + if (strcmp(cgutil_opt.mpoints[index], mpoint) == 0) { + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", mpoint); + securec_check_ss_c(rc, "\0", "\0"); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", mpoint); + return -1; + } + fprintf(stderr, "LOG: umount cgroup under %s!\n", mpoint); + } + else if (index == MOUNT_CPU_ID || index == MOUNT_CPUACCT_ID) { + // 瀵逛簬CPU瀛愮郴缁熷拰CPUACCT瀛愮郴缁燂紝妫鏌ユ柊鐨勬寕杞界偣鐩綍锛屽苟绉婚櫎宸插瓨鍦ㄧ殑绗﹀彿閾炬帴 + RemoveExistSymbolLink(mpoint); + + if (index == MOUNT_CPU_ID) { + rc = snprintf_s(mpoint, sizeof(mpoint), sizeof(mpoint) - 1, "%s/cpu,cpuacct", path); + securec_check_ss_c(rc, "\0", "\0"); + if (*cgutil_opt.mpoints[index] && strcmp(cgutil_opt.mpoints[index], mpoint) == 0) { + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", mpoint); + securec_check_ss_c(rc, "\0", "\0"); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", mpoint); + return -1; + } + } + } + } + + return 0; + } + + /* + * 鍑芥暟鍔熻兘锛氬嵏杞紺group鏂囦欢绯荤粺鐨勬牴鐩綍 + * 鍑芥暟鍙傛暟锛歷oid + * 鍑芥暟杩斿洖鍊硷細-1琛ㄧず寮傚父锛0琛ㄧず姝e父 + */ + int cgexec_umount_root_cgroup(void) + { + int i, ret; + char cmd[MAX_COMMAND_LENGTH]; + char* path = NULL; + errno_t rc; + + // 濡傛灉mpflag涓簍rue锛屽垯浣跨敤鎸囧畾鐨勬寕杞界偣璺緞锛涘惁鍒欎娇鐢ㄩ粯璁ょ殑GSCGROUP_MOUNT_POINT璺緞 + if (cgutil_opt.mpflag) + path = cgutil_opt.mpoint; + else + path = GSCGROUP_MOUNT_POINT; + + // 妫鏌ュ悗绔幆澧冨弬鏁版槸鍚﹀悎娉 + if (CheckBackendEnv(path) != 0) { + return -1; + } + for (i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + // 'blkio'涓烘棤鏁堝瓙绯荤粺锛屽拷鐣ヤ箣 + if (i == MOUNT_BLKIO_ID) + continue; + + if (*cgutil_opt.mpoints[i] == '\0') { + continue; + } + + // 濡傛灉Cgroup瀛愮郴缁熸寕杞藉湪鏃х殑榛樿鎸傝浇鐐逛笂锛屽垯鍙嵏杞戒竴娆 + if (strcmp(cgutil_opt.mpoints[i], GSCGROUP_MOUNT_POINT_OLD) == 0) { + rc = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "umount %s", GSCGROUP_MOUNT_POINT_OLD); + securec_check_ss_c(rc, "\0", "\0"); + + ret = system(cmd); + if (CheckSystemSucess(ret) == -1) { + fprintf(stderr, "ERROR: failed to umount cgroup under %s!\n", GSCGROUP_MOUNT_POINT_OLD); + return -1; + } + + break; + } + + // 璋冪敤CgexecUmountRootCgroupInternal鍑芥暟鍗歌浇Cgroup鏂囦欢绯荤粺 + ret = CgexecUmountRootCgroupInternal(path, i); + } + + return 0; + } + /* + * 鍑芥暟鍚嶏細cgexec_delete_cgroups + * 鍔熻兘锛氭牴鎹浉瀵硅矾寰勫垹闄group + * 鍙傛暟锛 + * relpath: 鐩稿璺緞 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + * 娉ㄦ剰锛氬綋鍒犻櫎涓涓狢group鏃朵娇鐢ㄨ鍑芥暟銆 + */ + int cgexec_delete_cgroups(char* relpath) + { + int ret; + struct cgroup* cg = NULL; + + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯 */ + cg = cgroup_new_cgroup(relpath); + if (cg == NULL) { + fprintf(stdout, "閿欒锛氫负%s鍒涘缓鏂扮殑cgroup澶辫触\n", relpath); + return -1; + } + + /* 浠庡唴鏍镐腑鑾峰彇鍏充簬cgroup鐨勬墍鏈変俊鎭 */ + ret = cgroup_get_cgroup(cg); + if (ret != 0) { + fprintf( + stdout, "閿欒锛氳幏鍙%s鐨刢group淇℃伅澶辫触锛岄敊璇爜锛%d锛岄敊璇俊鎭細%s\n", relpath, ret, cgroup_strerror(ret)); + cgroup_free(&cg); + return -1; + } + + (void)cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_RECURSIVE | CGFLAG_DELETE_IGNORE_MIGRATION); + + cgroup_free(&cg); + + return 0; + } + + /* + * 鍑芥暟鍚嶏細cgexec_create_groups + * 鍔熻兘锛氬垱寤篊group鐨勪富鍏ュ彛锛 + * 褰撶敤鎴锋槸root鏃讹紝闇瑕佹鏌groups鏄惁宸茬粡鍒涘缓銆傚鏋滄病鏈夛紝灏嗗垱寤洪粯璁ょ殑Cgroups銆 + * 褰撶敤鎴锋槸闈瀝oot鐢ㄦ埛鏃讹紝濡傛灉Cgroups涓嶅瓨鍦紝鍒欐姤閿欍 + * 鍙兘鍒涘缓Class鍜學orkload Cgroup锛屼絾涓嶅厑璁镐负DefaultClass Cgroup鍒涘缓workload Cgroup銆 + * 杩斿洖鍊硷細 + * -1: 寮傚父 + * 0: 姝e父 + * + */ + int cgexec_create_groups(void) + { + int cgcnt; + struct stat buf; + int ret; + size_t len = sizeof(cgutil_opt.mpoint) + 1 + sizeof(GSCGROUP_TOP_DATABASE) + 1 + USERNAME_LEN + 1 + + sizeof(GSCGROUP_TOP_CLASS); + char* cgpath = (char*)malloc(len); + errno_t sret; + + if (cgpath == NULL) + return -1; + + sret = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); + securec_check_errno(sret, free(cgpath), -1); + + if (geteuid() == 0) { + /* 鍒涘缓cm cgroup锛屾鍑芥暟鍙互妫鏌ョ洰褰曟槸鍚﹀瓨鍦紝鍥犳鎴戜滑涓嶉渶瑕佸厛杩涜妫鏌 */ + (void)cgexec_create_cm_default_cgroup(); + + cgcnt = cgexec_get_cgroup_number(); + if (1 == cgcnt) { + /* 鍒涘缓榛樿鐨刢groups */ + (void)cgexec_create_default_cgroups(); + } + else { + /* 妫鏌ユ槸鍚︿负鎸囧畾鐢ㄦ埛鍒涘缓浜哻groups */ + sret = sprintf_s(cgpath, + len, + "%s/%s:%s/%s", + cgutil_opt.mpoints[MOUNT_CPU_ID], + GSCGROUP_TOP_DATABASE, + cgutil_opt.user, + GSCGROUP_TOP_CLASS); + securec_check_intval(sret, free(cgpath), -1); + + ret = stat(cgpath, &buf); + + // 瑙i噴锛氭鏌SCGROUP_TOP_DATABASE鐩綍涓嬫槸鍚﹀瓨鍦╟gutil_opt.user鐩綍锛屽鏋滀笉瀛樺湪锛屽垯琛ㄧずCgroups鏈鍒涘缓銆 + if (ret != 0) { + fprintf(stdout, "閿欒锛氭棤娉曞湪%s鐩綍涓嬫壘鍒%s鐩綍\n", GSCGROUP_TOP_DATABASE, cgutil_opt.user); + free(cgpath); + return -1; + } + } + } + else { + /* 闈瀝oot鐢ㄦ埛蹇呴』妫鏌groups鏄惁瀛樺湪锛屼笉瀛樺湪鍒欐姤閿 */ + sret = sprintf_s(cgpath, + len, + "%s/%s:%s/%s", + cgutil_opt.mpoints[MOUNT_CPU_ID], + GSCGROUP_TOP_DATABASE, + cgutil_opt.user, + GSCGROUP_TOP_CLASS); + securec_check_intval(sret, free(cgpath), -1); + + ret = stat(cgpath, &buf); + + // 瑙i噴锛氭鏌SCGROUP_TOP_DATABASE鐩綍涓嬫槸鍚﹀瓨鍦╟gutil_opt.user鐩綍锛屽鏋滀笉瀛樺湪锛屽垯琛ㄧずCgroups鏈鍒涘缓銆 + if (ret != 0) { + fprintf(stdout, "閿欒锛氭棤娉曞湪%s鐩綍涓嬫壘鍒%s鐩綍\n", GSCGROUP_TOP_DATABASE, cgutil_opt.user); + free(cgpath); + return -1; + } + } + + free(cgpath); + return 0; + } + + /** + * 鍑芥暟鍚嶇О锛歝gexec_drop_nodegroup_cgroups + * 鎻忚堪锛氬垹闄ゆ寚瀹氳妭鐐圭粍鐨凜group + * + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + int cgexec_drop_nodegroup_cgroups(void) + { + /* 鍒犻櫎閰嶇疆鏂囦欢 */ + char* cfgpath = NULL; + + cfgpath = cgconf_get_config_path(false); // 鑾峰彇閰嶇疆鏂囦欢璺緞 + + if (cfgpath != NULL && !cgutil_opt.rename) { + (void)unlink(cfgpath); // 鍒犻櫎閰嶇疆鏂囦欢 + } + else if (cfgpath == NULL) { + return -1; + } + + /* 鍒犻櫎鏈娇鐢ㄧ殑澶囦唤鏂囦欢 */ + cgconf_remove_backup_conffile(); // 绉婚櫎鏈娇鐢ㄧ殑澶囦唤鏂囦欢 + + /* 鍒犻櫎鑺傜偣缁勭殑Cgroup鏍 */ + (void)cgptree_drop_nodegroup_cgroups(cgutil_opt.nodegroup); // 鍒犻櫎鑺傜偣缁勭殑Cgroup鏍 + + if (cgutil_opt.rename) { + void* vaddr = NULL; + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); + errno_t sret; + + /* 鍒犻櫎鏃х殑Cgroup */ + (void)cgptree_drop_nodegroup_cgroups(GSCGROUP_TOP_CLASS); // 鍒犻櫎鏃х殑Cgroup + + /* 鏄犲皠鍘熷Cgroup閰嶇疆鏂囦欢 */ + vaddr = cgconf_map_origin_conffile(); // 鏄犲皠鍘熷Cgroup閰嶇疆鏂囦欢 + if (vaddr == NULL) { + fprintf(stderr, "ERROR: failed to create and map the configure file!\n"); + free(cfgpath); + cfgpath = NULL; + return -1; + } + + for (int i = 0; i < CLASSCG_START_ID; i++) { + gscgroup_grp_t* tmpvaddr = (gscgroup_grp_t*)vaddr + i; + sret = memcpy_s(cgutil_vaddr[i], sizeof(gscgroup_grp_t), tmpvaddr, sizeof(gscgroup_grp_t)); + securec_check_errno(sret, (void)munmap(vaddr, cglen); free(cfgpath); , -1); + } + + /* 鍙栨秷鏄犲皠榛樿缁勭殑vaddr */ + (void)munmap(vaddr, GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); + + current_nodegroup = NULL; + + /* 鏍规嵁鏄犲皠淇℃伅鍒涘缓鑺傜偣缁勯粯璁group */ + if (-1 == cgexec_create_nodegroup_default_cgroups()) { + free(cfgpath); + cfgpath = NULL; + return -1; + } + + cgutil_opt.nodegroup[0] = '\0'; // 閲嶇疆鑺傜偣缁勪俊鎭 + + /* 閲嶅懡鍚嶉厤缃枃浠 */ + char* old_confpath = cgconf_get_config_path(false); + if (old_confpath == NULL) { + fprintf(stderr, "ERROR: failed to get the configuration path,configuration path is NULL."); + free(cfgpath); + cfgpath = NULL; + return -1; + } + + if (-1 == rename(cfgpath, old_confpath)) { + fprintf(stderr, "ERROR: failed to rename %s to %s.", cfgpath, old_confpath); + free(cfgpath); + cfgpath = NULL; + free(old_confpath); + old_confpath = NULL; + return -1; + } + free(old_confpath); + old_confpath = NULL; + } + + free(cfgpath); + cfgpath = NULL; + return 0; + } + + /** + * 鍑芥暟鍚嶇О锛歝gexec_drop_groups + * 鎻忚堪锛氬綋娌℃湁鎸囧畾鐨凜lass缁勬椂锛岃秴绾х敤鎴峰皢鍒犻櫎Gaussdb缁勩傚惁鍒欙紝灏嗗垹闄ゆ寚瀹氱殑Class缁勩 + * 褰撴寚瀹氣-M鈥濋夐」鏃讹紝瀹冧細鍗歌浇Cgroup鏂囦欢绯荤粺銆 + * + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + int cgexec_drop_groups(void) + { + /* 瓒呯骇鐢ㄦ埛鍒犻櫎鏅氱敤鎴风殑Cgroup骞跺嵏杞 */ + if (cgutil_opt.user[0] != '\0' && geteuid() == 0 && cgutil_opt.clsname[0] == '\0') { + (void)cgptree_drop_cgroups(); // 鍒犻櫎Cgroup骞跺嵏杞 + } + + /* 瓒呯骇鐢ㄦ埛鍒犻櫎cm cgroup */ + if (cgutil_opt.user[0] != '\0' && geteuid() == 0) { + (void)cgexec_delete_cm_cgroup(); // 鍒犻櫎cm cgroup + } + + /* 闈炶秴绾х敤鎴峰垹闄ゆ寚瀹氳妭鐐圭粍鐨凜group */ + if (geteuid() != 0 && cgutil_opt.nodegroup[0] != '\0' && cgutil_opt.clsname[0] == '\0') { + (void)cgexec_drop_nodegroup_cgroups(); // 鍒犻櫎鎸囧畾鑺傜偣缁勭殑Cgroup + } + + if (cgutil_opt.clsname[0] != '\0') { + if (strcmp(cgutil_opt.clsname, GSCGROUP_DEFAULT_CLASS) == 0) { + fprintf(stderr, "ERROR: Can't drop DefaultClass Cgroup!\n"); + return -1; + } + + if (-1 == cgexec_delete_class_cgroup()) { + cgconf_remove_backup_conffile(); + } + } + + if (geteuid() == 0 && cgutil_opt.umflag) { + fprintf(stdout, "ERROR: Cgroup is mounted. Ready to umount cgroup!\n"); + + /* 鎸傝浇Cgroup */ + (void)cgexec_umount_root_cgroup(); + } + + return 0; + } + */ + + /** + * 瑙f瀽锛 + * 绗竴涓嚱鏁癱gexec_drop_nodegroup_cgroups鐢ㄤ簬鍒犻櫎鎸囧畾鑺傜偣缁勭殑Cgroup銆 + * 璇ュ嚱鏁伴鍏堣幏鍙栭厤缃枃浠惰矾寰勶紝鐒跺悗鍒ゆ柇鏄惁闇瑕佸垹闄ら厤缃枃浠讹紝濡傛灉闇瑕佸垯鍒犻櫎銆 + * 鎺ヤ笅鏉ュ垹闄ゆ湭浣跨敤鐨勫浠芥枃浠躲傚啀娆¤皟鐢╟gptree_drop_nodegroup_cgroups鍑芥暟鍒犻櫎鑺傜偣缁勭殑Cgroup鏍戙 + * 濡傛灉闇瑕侀噸鍛藉悕閰嶇疆鏂囦欢锛屽垯杩涜涓绯诲垪鐨勬搷浣滐紝鍖呮嫭鍒犻櫎鏃х殑Cgroup骞舵槧灏勫師濮嬮厤缃枃浠讹紝鍒涘缓鑺傜偣缁勯粯璁group锛岄噸鍛藉悕閰嶇疆鏂囦欢銆 + * 鏈鍚庨噴鏀惧唴瀛樺苟杩斿洖缁撴灉銆 + * + * 绗簩涓嚱鏁癱gexec_drop_groups鐢ㄤ簬鍒犻櫎Cgroup銆傚嚱鏁伴鍏堝垽鏂槸鍚﹂渶瑕佸垹闄ゆ櫘閫氱敤鎴风殑Cgroup骞跺嵏杞姐 + * 鐒跺悗鍒ゆ柇鏄惁闇瑕佸垹闄m cgroup銆傛帴涓嬫潵鍒ゆ柇鏄惁闇瑕佸垹闄ら潪瓒呯骇鐢ㄦ埛鎸囧畾鑺傜偣缁勭殑Cgroup銆 + * 濡傛灉鎸囧畾浜咰lass缁勶紝涓斾负DefaultClass锛屽垯杩斿洖寮傚父銆傜劧鍚庡垽鏂槸鍚﹂渶瑕佸垹闄ゆ寚瀹氱殑Class缁勩 + * 鏈鍚庡垽鏂槸鍚﹂渶瑕佸嵏杞紺group骞惰繑鍥炵粨鏋溿 + */ + /* + * 鍑芥暟鍚嶇О锛歝gexec_update_groups + * 鍔熻兘鎻忚堪锛氭牴鎹夐」鏇存柊鍔ㄦ佸兼垨鍥哄畾鍊 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + int cgexec_update_groups(void) + { + int ret = 0; + + /* 澶囦唤閰嶇疆鏂囦欢 */ + if (-1 == cgconf_backup_config_file()) + return -1; + + if (0 == cgutil_opt.fixed) { + if ('\0' != cgutil_opt.clsname[0]) + ret = cgexec_update_dynamic_class_cgroup(); + + if ('\0' != cgutil_opt.bkdname[0]) + ret = cgexec_update_dynamic_backend_cgroup(); + + if ('\0' != cgutil_opt.topname[0]) + ret = cgexec_update_dynamic_top_cgroup(); + } + else if (cgutil_opt.fixed) { + if ('\0' != cgutil_opt.clsname[0]) + ret = cgexec_update_fixed_class_cgroup(); + + if ('\0' != cgutil_opt.bkdname[0]) + ret = cgexec_update_fixed_backend_cgroup(); + + if ('\0' != cgutil_opt.topname[0]) + ret = cgexec_update_fixed_top_cgroup(); + } + + /* 绉婚櫎澶囦唤鏂囦欢 */ + if (-1 == ret) { + cgconf_remove_backup_conffile(); + } + + return 0; + } + + /* 鑾峰彇Root淇℃伅 */ + int cgexec_get_cgroup_cpuset_info(int cnt, char** cpuset) + { + char* relpath = NULL; + struct cgroup* cg = NULL; + struct cgroup_controller* cgc_cpu = NULL; + int ret; + + /* 鑾峰彇鐩稿璺緞 */ + if (NULL == (relpath = gscgroup_get_relative_path(cnt, cgutil_vaddr, current_nodegroup))) + return -1; + + /* 鍒嗛厤鏂扮殑cgroup缁撴瀯 */ + cg = cgroup_new_cgroup(relpath); + if (cg == NULL) { + fprintf(stdout, "ERROR: failed to create the new cgroup for %s\n", relpath); + free(relpath); + relpath = NULL; + return -1; + } + + /* 浠庡唴鏍歌幏鍙栧叧浜巆group鐨勬墍鏈変俊鎭 */ + ret = cgroup_get_cgroup(cg); + if (ret != 0) { + fprintf(stdout, "ERROR: failed to get %s cgroup information for %s(%d)\n", relpath, cgroup_strerror(ret), ret); + cgroup_free(&cg); + free(relpath); + relpath = NULL; + return -1; + } + + free(relpath); + relpath = NULL; + + /* 鑾峰彇CPU鎺у埗鍣 */ + cgc_cpu = cgroup_get_controller(cg, MOUNT_CPUSET_NAME); + if (NULL == cgc_cpu) { + fprintf(stderr, "ERROR: failed to add %s controller in %d!\n", MOUNT_CPU_NAME, cnt); + cgroup_free(&cg); + return -1; + } + + /* 閫氳繃鎺у埗鍣ㄨ幏鍙朿puset鍊 */ + if (0 != (ret = cgroup_get_value_string(cgc_cpu, CPUSET_CPUS, cpuset))) { + fprintf(stderr, "ERROR: failed to get %s for %s\n", CPUSET_CPUS, cgroup_strerror(ret)); + goto error; + } + + /* 鑾峰彇mems淇℃伅 */ + if (cnt == TOPCG_ROOT) { + char* cpumems = NULL; + if (0 != (ret = cgroup_get_value_string(cgc_cpu, CPUSET_MEMS, &cpumems))) { + fprintf(stderr, "ERROR: failed to get %s for %s\n", CPUSET_MEMS, cgroup_strerror(ret)); + goto error; + } + + errno_t sret = snprintf_s(cgutil_mems, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpumems); + securec_check_intval(sret, free(cpumems); cgroup_free_controllers(cg); cgroup_free(&cg), -1); + + free(cpumems); + cpumems = NULL; + } + + cgroup_free_controllers(cg); + cgroup_free(&cg); + return 0; + error: + cgroup_free_controllers(cg); + cgroup_free(&cg); + return -1; + } + + /* + * @鎻忚堪锛氳幏鍙栧綋鍓嶅唴瀛樿鏁般 + * @OUT mems锛氬唴瀛橀泦鍚 + * @IN size锛氬唴瀛橀泦鍚堝ぇ灏 + * @杩斿洖鍊硷細鍐呭瓨闆嗗悎 + * @鍙傝冿細 + */ + char* cgexec_get_cgroup_cpuset_mems(char* mems, int size) + { + int ret = 1; + char cmd[128]; + char line[128]; + + FILE* fp = NULL; + + /* 鎵撳紑'/proc/cpuinf'浠ユ悳绱'physical id'璁℃暟浠ヨ幏鍙栧唴瀛橀泦鍚 */ + errno_t sret = snprintf_s(cmd, + sizeof(cmd), + sizeof(cmd) - 1, + "%s", + "lscpu | grep \"NUMA node(s)\" | awk -F: '{print $2}'| sed 's/\\ //g'"); + securec_check_intval(sret, , mems); + + if ((fp = popen(cmd, "r")) != NULL) { + if (fgets(line, sizeof(line), fp) != NULL) { + /* 鑾峰彇璁℃暟 */ + ret = atoi(line); + + if (ret == 0) + ret = 1; + } + + pclose(fp); + } + + /* 鑾峰彇鍐呭瓨闆嗗悎 */ + sret = snprintf_s(mems, size, size - 1, "%d-%d", 0, ret - 1); + securec_check_intval(sret, , mems); + + return mems; + } + /* + * @Description: 鏇存柊閰嶇疆鏂囦欢鐨刢puset + * @IN void + * @Return: -1: 寮傚父 0: 姝e父 + * @See also: + */ + int cgexec_check_top_cpuset(void) + { + /* + * 妫鏌ラ厤缃枃浠剁殑鏈澶ф暟閲忔槸鍚︿笌褰撳墠鑺傜偣鐨勬绘牳蹇冩暟鍏煎銆 + * 濡傛灉鍏煎锛屽垯鏃犻渶鏇存柊銆 + */ + if (cgexec_check_cpuset_value(cgutil_allset, cgutil_vaddr[TOPCG_GAUSSDB]->cpuset) == 0) + return 0; + + /* 濡傛灉涓嶅吋瀹癸紝鍒欓渶瑕佹洿鏂伴厤缃枃浠 */ + for (int i = 0; i <= WDCG_END_ID; ++i) { + if (cgutil_vaddr[i]->used == 0) + continue; + + errno_t sret = snprintf_s(cgutil_vaddr[i]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cgutil_allset); + securec_check_intval(sret, , -1); + } + + return 0; + } + + /* + * @Description : 閫掑綊鏇存柊CPU鏍稿績鐧惧垎姣斿拰cpuset鍊 + * : 鍏堝墠鐨勨-f鈥濊鈥--fixed鈥濆彇浠o紝 + * : 鍏锋湁鈥渃pusets鈥濈殑缁勯兘瑕佽浆鎹负楂樼骇绾у埆鐨勭櫨鍒嗘瘮 + * @IN high : 楂樼骇鍒粍鐨処D + * @IN extended : 鎬婚厤棰濊秴鍑鸿寖鍥达紝鎵浠ョ粍鐨刢pusets鍜屾墍灞炵粍鐨刢pusets閮戒笌楂樼骇鍒粍鐩稿悓锛 + * : 闄や簡閭d簺宸茬粡璁剧疆浜嗏渜uota鈥濈殑缁 + * @Return : + * @See also: + */ + static void cgexec_update_fixed_config(int high, int extended) + { + int forstart = 0, forend = 0; /* 寰幆鐨勫紑濮嬪拰缁撴潫鍊 */ + int i = 0; + int lowlen = 0, highlen = 0; /* 浣庣骇鍒拰楂樼骇鍒玞puset鐨勯暱搴 */ + int start = 0, end = 0; /* 浠呯敤浜庤皟鐢ㄥ嚱鏁癱gexec_get_cpuset_length */ + int lowstart = 0, lowend = 0; /* 浣庣骇鍒粍cpuset鐨勮捣濮嬪拰缁撴潫鍊 */ + int highstart = 0, highend = 0; /* 楂樼骇鍒粍cpuset鐨勮捣濮嬪拰缁撴潫鍊 */ + int part_quota = 0; /* 浠巆pusets杞崲鐨勫綋鍓嶉厤棰 */ + int sum_quota = 0; /* 閰嶉鍊肩殑鎬诲拰 */ + errno_t sret = 0; /* securec_check 鐨勮繑鍥炲 */ + char sets[CPUSET_LEN]; /* 瑕佹洿鏂扮殑璁$畻鍚庣殑cpuset */ + bool flag = false; /* 琛ㄧず绗竴娆¤繘鍏ュ惊鐜殑鏍囧織 */ + char topwd[GPNAME_LEN]; + + /* 鑾峰彇 'topwd' 鎺у埗缁勭殑瀹屾暣鍚嶇О */ + sret = snprintf_s(topwd, sizeof(topwd), sizeof(topwd) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); + securec_check_intval(sret, , ); + + /* 濡傛灉楂樼骇鍒凡缁忔槸鏈浣庣骇鍒紝鍒欎腑姝㈤掑綊 */ + if (cgexec_get_cgroup_id_range(high, &forstart, &forend) == -1) + return; + + /* 楂樼骇鍒粍鐨刢puset闀垮害 */ + highlen = cgexec_get_cpuset_length(cgutil_vaddr[high]->cpuset, &highstart, &highend); + + sum_quota = cgexec_check_fixed_percent(high); + + for (i = forstart; i <= forend; i++) { + ... + } + } + / * + *鍑芥暟鍚嶇О锛歝gexec_refresh_groups_internal + * 鎻忚堪锛氬埛鏂扮粍鍐呴儴鍑芥暟 + * 杩斿洖鍊硷細 + * -1锛氬紓甯 + * 0锛氭甯 + * + */ + static int cgexec_refresh_groups_internal(void) + { + // 閫掑綊鏇存柊鎵鏈夋帶鍒剁粍鐨勯厤棰濆拰cpuset + cgexec_update_fixed_config(TOPCG_GAUSSDB, 0); + + / *鍒涘缓榛樿缁勩 + * 濡傛灉鍙戠敓閿欒锛屽垯杩斿洖 - 1銆 + * / + if (cgexec_create_default_cgroups()) { + return -1; + } + if (cgexec_create_cm_default_cgroup()) { + return -1; + } + + return 0; + } + + /* + *@鎻忚堪锛氫娇鐢ㄩ厤缃枃浠跺埛鏂癱group銆 + * @ IN澶у皬锛歷oid + * @杩斿洖锛 - 1锛氬紓甯 0锛氭甯 + * @涔熷弬瑙侊細 + */ + //鍑芥暟鍔熻兘锛 + // 1. cgexec_refresh_groups_internal锛氬埛鏂扮粍鍐呴儴鍑芥暟銆傚畠閫掑綊鏇存柊鎵鏈夋帶鍒剁粍鐨勯厤棰濆拰cpuset锛屽苟鍒涘缓榛樿缁勩 + // 2. cgexec_refresh_original_groups锛氫娇鐢ㄩ厤缃枃浠跺埛鏂癱group銆傚畠鍒犻櫎鍚庣鍜岀被缁勶紝妫鏌ュ綋鍓嶈妭鐐圭殑鏍稿績鏁版槸鍚︿笌閰嶇疆鐨勬渶澶ф牳蹇冩暟鍏煎锛岀劧鍚庡垱寤洪粯璁groups銆 + // 绫讳技鐨勫簲鐢ㄥ疄渚嬶細 + // - 鍦ㄤ竴涓垎甯冨紡绯荤粺涓紝浣跨敤cgroups鏉ョ鐞嗕换鍔″垎閰嶅拰璧勬簮闄愬埗銆傚綋闇瑕佸埛鏂癱group鏃讹紝鍙互浣跨敤cgexec_refresh_original_groups鏉ユ洿鏂扮粍鐨勯厤缃紝浠ラ傚簲鏂扮殑鑺傜偣鎴栬祫婧愰檺鍒躲 + // - 鍦ㄥ鍣ㄥ寲鐜涓紝浣跨敤cgroups鏉ラ檺鍒跺鍣ㄧ殑璧勬簮浣跨敤銆傚綋闇瑕佸埛鏂癱group鏃讹紝鍙互浣跨敤cgexec_refresh_original_groups鏉ラ噸鏂板垱寤洪粯璁groups锛屽苟鏍规嵁鏂扮殑璧勬簮闇姹傝繘琛岄厤缃洿鏂般 + int cgexec_refresh_original_groups(void) + { + /*鍒犻櫎鍚庣鍜岀被缁 */ + for (int idx = TOPCG_BACKEND; idx <= TOPCG_CLASS; ++idx) { + if (cgutil_vaddr[idx]->used == 0) + 缁х画; + + if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[idx])) + return -1; + } + + /* + *鎴戜滑蹇呴』妫鏌ュ綋鍓嶈妭鐐逛笂鏄惁鍙互浣跨敤閰嶇疆鐨勬渶澶ф牳蹇冩暟锛屽鏋滀笉琛岋紝鎴戜滑灏嗗皾璇曟洿鏂伴厤缃枃浠朵互涓庢柊鑺傜偣鍏煎 + */ + if (cgexec_check_top_cpuset() == -1) { + fprintf(stderr, "閿欒锛氭洿鏂伴《绾puset閿欒銆"); + return -1; + } + + /*鍒涘缓榛樿cgroups */ + return cgexec_refresh_groups_internal(); + } + /* + *@鎻忚堪锛氫娇鐢ㄨ妭鐐圭粍鐨勯厤缃枃浠跺埛鏂癱group銆 + * @IN size锛歷oid + * @杩斿洖锛 - 1锛氬紓甯 0锛氭甯 + * @涔熷弬瑙侊細 + */ + //鍑芥暟鍔熻兘锛 + // cgexec_refresh_nodegroup_groups锛氫娇鐢ㄨ妭鐐圭粍鐨勯厤缃枃浠跺埛鏂癱group銆傚畠鍒犻櫎鑺傜偣缁勭殑閫昏緫闆嗙兢缁勶紝鐒跺悗鍒涘缓榛樿cgroups銆 + // 绫讳技鐨勫簲鐢ㄥ疄渚嬶細 + // - 鍦ㄤ竴涓垎甯冨紡绯荤粺涓紝浣跨敤cgroups鏉ョ鐞嗚妭鐐圭粍鐨勮祫婧愪娇鐢ㄣ傚綋闇瑕佹牴鎹柊鐨勮妭鐐圭粍閰嶇疆鍒锋柊cgroup鏃讹紝鍙互浣跨敤cgexec_refresh_nodegroup_groups鏉ラ噸鏂板垱寤洪粯璁groups锛屽苟鍒犻櫎鏃х殑閫昏緫闆嗙兢缁勩 + int cgexec_refresh_nodegroup_groups(void) + { + /*鍒犻櫎閫昏緫闆嗙兢缁 */ + if (-1 == cgptree_drop_nodegroup_cgroups(cgutil_opt.nodegroup)) + 杩斿洖 - 1; + + /*鍒涘缓榛樿cgroups */ + return cgexec_create_nodegroup_default_cgroups(); + } + + /* + *@鎻忚堪锛氫娇鐢ㄩ厤缃枃浠跺埛鏂癱group銆 + * @IN澶у皬锛歷oid + * @杩斿洖锛 - 1锛氬紓甯 0锛氭甯 + * @涔熷弬瑙侊細 + */ + //鍑芥暟鍔熻兘锛 +// cgexec_refresh_groups锛氫娇鐢ㄩ厤缃枃浠跺埛鏂癱group銆傚畠鏍规嵁閰嶇疆鏂囦欢鐨勫唴瀹规潵鍒锋柊cgroup锛屽彲浠ユ牴鎹妭鐐圭粍鐨勯厤缃垨鑰呴粯璁ょ殑閰嶇疆鏉ュ埛鏂般 +// 绫讳技鐨勫簲鐢ㄥ疄渚嬶細 +// - 鍦ㄤ竴涓泦缇ょ幆澧冧腑锛屼娇鐢╟groups鏉ョ鐞嗕换鍔″垎閰嶅拰璧勬簮闄愬埗銆傚綋闇瑕佸埛鏂癱group鏃讹紝鍙互鏍规嵁鑺傜偣缁勭殑閰嶇疆浣跨敤cgexec_refresh_groups鏉ユ洿鏂癱group鐨勯厤缃紝浠ラ傚簲涓嶅悓鐨勮妭鐐圭粍鎴栬祫婧愰渶姹傘 + int cgexec_refresh_groups(void) + { + if ('\0' == cgutil_opt.nodegroup[0]) + return cgexec_refresh_original_groups(); + else + return cgexec_refresh_nodegroup_groups(); + } + + /* + *@鎻忚堪锛氳繕鍘焎group鐨勯厤缃 + * @IN澶у皬锛歷oid + * @杩斿洖锛 - 1锛氬紓甯 0锛氭甯 + * @涔熷弬瑙侊細 + */ + /*鍑芥暟鍔熻兘锛 + cgexec_revert_groups锛氳繕鍘焎group鐨勯厤缃傚畠鍒犻櫎闄や簡榛樿绫荤粍涔嬪鐨勬墍鏈夌被缁勶紝閲嶇疆鎵鏈夌粍鐨勯厤缃紝骞惰繕鍘熼厤缃枃浠讹紝鐒跺悗鍒涘缓榛樿缁勩 + 绫讳技鐨勫簲鐢ㄥ疄渚嬶細 + - 鍦ㄤ竴涓鍣ㄥ寲鐜涓紝浣跨敤cgroups鏉ョ鐞嗗鍣ㄧ殑璧勬簮浣跨敤銆傚綋闇瑕佽繕鍘焎group鐨勯厤缃椂锛屽彲浠ヤ娇鐢╟gexec_revert_groups鏉ュ垹闄ゆ棫鐨勭粍锛岄噸缃厤缃紝骞堕噸鏂板垱寤洪粯璁ょ粍锛屼互杩樺師鍒板垵濮嬬殑璧勬簮閰嶇疆銆 */ + int cgexec_revert_groups(void) + { + for (int cls = CLASSCG_START_ID + 1; cls <= CLASSCG_END_ID; ++cls) { + if (cgutil_vaddr[cls]->used == 0) + 缁х画; + + /*鍒犻櫎闄や簡榛樿绫荤粍涔嬪鐨勬墍鏈夌被缁*/ + if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls])) + 杩斿洖-1; + + /*閲嶇疆鎵鏈夌粍鐨勯厤缃*/ + cgconf_reset_class_group(cls); + } + + /*杩樺師閰嶇疆鏂囦欢*/ + cgconf_revert_config_file(); + + /*鍒涘缓榛樿缁勩 + * 濡傛灉鍙戠敓閿欒锛屽垯杩斿洖-1銆 + */ + if (cgexec_create_default_cgroups()) + return -1; + if (cgexec_create_cm_default_cgroup()) + return -1; + + return 0; + } + + /* + * @鎻忚堪锛氭鏌ヤ袱涓粍鏄惁鍙戠敓浜嗗彉鍖栥 + * @IN cur锛氬綋鍓嶇粍 + * @IN bak锛氬浠界粍 + * @杩斿洖锛 1锛氬凡鏇存柊 0锛氭湭鏇存柊 + * @涔熷弬瑙侊細 + */ + /*鍑芥暟鍔熻兘锛 + cgexec_check_update_groups锛氭鏌ヤ袱涓粍鏄惁鍙戠敓浜嗗彉鍖栥傚畠姣旇緝涓や釜缁勭殑alloc_info_t缁撴瀯浣擄紝骞惰繑鍥炴槸鍚﹀彂鐢熶簡鍙樺寲銆 + 绫讳技鐨勫簲鐢ㄥ疄渚嬶細 + - 鍦ㄤ竴涓郴缁熶腑锛屼娇鐢╟groups鏉ラ檺鍒剁▼搴忕殑璧勬簮浣跨敤銆傚綋闇瑕佹鏌ヤ袱涓粍鐨勮祫婧愬垎閰嶆槸鍚﹀彂鐢熷彉鍖栨椂锛屽彲浠ヤ娇鐢╟gexec_check_update_groups鏉ユ瘮杈冧袱涓粍鐨刟lloc_info_t缁撴瀯浣擄紝浠ョ‘瀹氭槸鍚﹂渶瑕佹洿鏂拌祫婧愰厤缃 */ + int cgexec_check_update_groups(gscgroup_grp_t* cur, gscgroup_grp_t* bak) + { + int offset = offsetof(gscgroup_grp_t, ainfo); + int size = sizeof(alloc_info_t); + + if (0 == memcmp((void*)((char*)cur + offset), (void*)((char*)bak + offset), size)) + return 0; + else + return 1; + } + + + /* + * @Description: 閫氳繃鏇存柊鐧惧垎姣旂粍鏉ユ仮澶嶇粍 + * @IN id: 瑕佹洿鏂扮殑缁勭殑ID + * @Return: + * -1: 寮傚父 0: 姝e父 + * @See also: + */ + int cgexec_recover_update_percent_groups(int id) + { + errno_t sret; + + /* 濡傛灉鏄被缁勫彉鍔 */ + if (id >= CLASSCG_START_ID && id <= CLASSCG_END_ID) { + /* 鏇存柊cgroup鍊 */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[id])) + return -1; + } + else if (id >= WDCG_START_ID && id <= WDCG_END_ID) { + int cls = cgutil_vaddr_back[id]->ginfo.wd.cgid; + + /* 闇瑕佹洿鏂版墍鏈夌殑绫荤粍鍙婂叾宸ヤ綔璐熻浇缁 */ + sret = memcpy_s(cgutil_vaddr[cls], sizeof(gscgroup_grp_t), cgutil_vaddr_back[cls], sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , -1); + + /* 鏇存柊OS cgroups */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[id])) + return -1; + + /* 闇瑕佹洿鏂板伐浣滆礋杞界粍 */ + sret = memcpy_s(cgutil_vaddr[id], sizeof(gscgroup_grp_t), cgutil_vaddr_back[id], sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , -1); + + /* 鏇存柊鍓╀綑缁 */ + if (-1 == cgexec_update_remain_cgroup(cgutil_vaddr[id], cls)) + return -1; + } + + return 0; + } + + /* + * @Description: 閫氳繃鏇存柊鍥哄畾鐨勭被缁勬潵鎭㈠缁 + * @IN id: 瑕佹洿鏂扮殑缁勭殑ID + * @IN cpuset: 杈撳叆鐨刢puset瀛楃涓 + * @IN reverse: 鏍囧織浣嶏紝琛ㄧず鏄惁鍙嶅悜 + * @Return: + * -1: 寮傚父 0: 姝e父 + * @See also: + */ + int cgexec_recover_update_fixed_class_group(int id, char* cpuset, int reverse) + { + errno_t sret; + + if (!reverse) /* 浠庝笅寰涓婃洿鏂 */ + { + /* 灏嗗煎鍒跺埌绫荤粍涓 */ + sret = strcpy_s(cgutil_vaddr[id]->cpuset, CPUSET_LEN, cpuset); + securec_check_errno(sret, , -1); + + /* 鏇存柊绫荤粍鍒癱group fs */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[id])) + return -1; + + /* 鏇存柊鍓╀綑缁 */ + for (int j = 1; j <= cgutil_vaddr[id]->ginfo.cls.maxlevel; ++j) { + if (-1 == cgexec_update_remain_cgroup_cpuset_value(id, j, cpuset)) + return -1; + } + + /* 鏇存柊鏃堕棿鍏变韩缁 */ + if (-1 == cgexec_update_timeshare_cpuset(cgutil_vaddr[id], cpuset, 0)) + return -1; + + /* 鏇存柊TopWD缁 */ + if (-1 == cgexec_update_topwd_cgroup_cpuset(id, cpuset)) + return -1; + } + else { + /* 鏇存柊鏃堕棿鍏变韩缁 */ + if (-1 == cgexec_update_timeshare_cpuset(cgutil_vaddr[id], cpuset, 1)) + return -1; + + /* 鏇存柊鍓╀綑缁 */ + for (int j = cgutil_vaddr[id]->ginfo.cls.maxlevel; j >= 1; --j) { + if (-1 == cgexec_update_remain_cgroup_cpuset_value(id, j, cpuset)) + return -1; + } + + /* 鏇存柊TopWD缁 */ + if (-1 == cgexec_update_topwd_cgroup_cpuset(id, cpuset)) + return -1; + + /* 灏嗗煎鍒跺埌绫荤粍涓 */ + sret = strcpy_s(cgutil_vaddr[id]->cpuset, GPNAME_LEN, cpuset); + securec_check_errno(sret, , -1); + + /* 鏇存柊绫荤粍鍒癱group fs */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr[id])) + return -1; + } + + return 0; + } + + /* + * @Description: 閫氳繃鏇存柊閰嶉缁勬潵鎭㈠缁 + * @IN id: 鏇存柊鐨勭粍鐨処D + * @Return: + * -1锛氬紓甯 0锛氭甯 + * @See also: + */ + int cgexec_recover_update_quota_groups(int id) + { + /* 濡傛灉鏄被缁勫彂鐢熷彉鍖 */ + if (id >= CLASSCG_START_ID && id <= CLASSCG_END_ID) { + /* 灏嗛粯璁ゅ奸噸缃负椤剁骇绫荤粍 */ + for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + /* 濡傛灉璇ョ被缁勬湭琚娇鐢ㄦ垨鑰咃紙涓嶆槸鏇存柊鐨勭粍涓旈厤棰濅负0锛夛紝鍒欒烦杩 */ + if (cgutil_vaddr[i]->used == 0 || (i != id && cgutil_vaddr[i]->ainfo.quota == 0)) + continue; + + /* 鍩轰簬澶囦唤鍊兼洿鏂板墿浣欏拰鏃堕棿鍏变韩缁 */ + if (-1 == cgexec_recover_update_fixed_class_group(i, cgutil_vaddr[TOPCG_CLASS]->cpuset, 0)) + return -1; + } + + /* 鏇存柊鎵鏈夌殑宸ヤ綔璐熻浇缁 */ + for (int j = WDCG_START_ID; j <= WDCG_END_ID; j++) { + /* 濡傛灉璇ュ伐浣滆礋杞界粍鏈浣跨敤鎴栬呭伐浣滆礋杞界粍鐨剋dlevel涓1锛屽垯璺宠繃 */ + if (cgutil_vaddr_back[j]->used == 0 || cgutil_vaddr_back[j]->ginfo.wd.wdlevel == 1) + continue; + + /* 鏇存柊宸ヤ綔璐熻浇缁 */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[j])) + return -1; + } + + /* 閲嶆柊鏇存柊绫荤粍 */ + for (int i = CLASSCG_START_ID; i <= CLASSCG_END_ID; i++) { + /* 濡傛灉璇ョ被缁勬湭琚娇鐢ㄦ垨鑰咃紙涓嶆槸鏇存柊鐨勭粍涓旈厤棰濅负0锛夛紝鍒欒烦杩 */ + if (cgutil_vaddr[i]->used == 0 || (i != id && cgutil_vaddr[i]->ainfo.quota == 0)) + continue; + + if (-1 == cgexec_recover_update_fixed_class_group(i, cgutil_vaddr_back[i]->cpuset, 1)) + return -1; + } + } + /* 濡傛灉鏄伐浣滆礋杞界粍鍙戠敓鍙樺寲 */ + else if (id >= WDCG_START_ID && id <= WDCG_END_ID) { + int cls = cgutil_vaddr_back[id]->ginfo.wd.cgid; + + for (int i = WDCG_START_ID; i <= WDCG_END_ID; i++) { + /* 濡傛灉璇ュ伐浣滆礋杞界粍鏈浣跨敤鎴栬呭伐浣滆礋杞界粍鐨刢gid涓嶇瓑浜巆ls锛屾垨鑰呭伐浣滆礋杞界粍鐨剋dlevel涓1锛屽垯璺宠繃 */ + if (cgutil_vaddr_back[i]->used == 0 || cgutil_vaddr_back[i]->ginfo.wd.cgid != cls || + cgutil_vaddr_back[i]->ginfo.wd.wdlevel == 1) + continue; + + /* 鏇存柊宸ヤ綔璐熻浇缁 */ + if (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[i])) + return -1; + } + } + + return 0; + } + + /* + * @Description: 閫氳繃鍒涘缓鏂扮殑cgroup鏉ユ仮澶嶇粍 + * @IN cls_add: 绫荤粍ID + * @IN wd_add: 鎵鏈夋柊鐨勫伐浣滆礋杞界粍鐨勬暟缁 + * @Return: + * -1锛氬紓甯 0锛氭甯 + * @See also: + */ + int cgexec_recover_create_groups(int cls_add, const int* wd_add) + { + int wld = 0, level = 0, i = 0, tmpcls; + int tmpwld[MAX_WD_LEVEL] = { 0 }; + errno_t sret; + + sret = memset_s(tmpwld, sizeof(tmpwld), 0, sizeof(tmpwld)); + securec_check_errno(sret, , -1); + + /* 鑾峰彇绫荤粍淇℃伅 */ + wld = wd_add[0]; + tmpcls = cgutil_vaddr_back[wld]->ginfo.wd.cgid; + + /* 楠岃瘉淇℃伅 */ + if (cls_add && cls_add != tmpcls) { + fprintf(stderr, "ERROR: 鏂板鐨勫伐浣滆礋杞界粍涓庣被缁勪笉鍖归厤锛乗n"); + return -1; + } + + if (cls_add == 0 && wd_add[1]) { + fprintf(stderr, + "ERROR: 褰撳彧鎭㈠宸ヤ綔璐熻浇缁勬椂锛屽彂鐜板涓柊澧炵殑宸ヤ綔璐熻浇缁勶紒\n"); + return -1; + } + + /* 鎼滅储宸ヤ綔璐熻浇缁 */ + for (i = WDCG_START_ID; i <= WDCG_END_ID; ++i) { + if (cgutil_vaddr_back[i]->used && cgutil_vaddr_back[i]->ginfo.wd.cgid == tmpcls) { + level = cgutil_vaddr_back[i]->ginfo.wd.wdlevel; + tmpwld[level - 1] = i; + } + } + + /* 濡傛灉鍙坊鍔犲伐浣滆礋杞界粍锛屽厛鍒犻櫎绫荤粍 */ + if (cls_add == 0 && -1 == cgexec_delete_default_cgroup(cgutil_vaddr[tmpcls])) + return -1; + + /* 澶嶅埗绫荤粍淇℃伅 */ + sret = memcpy_s(cgutil_vaddr[tmpcls], sizeof(gscgroup_grp_t), cgutil_vaddr_back[tmpcls], sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , -1); + + /* 閲嶇疆鍊 */ + cgutil_vaddr[tmpcls]->ginfo.cls.maxlevel = 0; + cgutil_vaddr[tmpcls]->ginfo.cls.rempct = 100; + cgconf_update_class_percent(); + + /* 鍒涘缓绫荤粍 */ + if (-1 == cgexec_create_new_cgroup(cgutil_vaddr[tmpcls])) { + cgconf_reset_class_group(tmpcls); + return -1; + } + + /* 寰幆鍒涘缓宸ヤ綔璐熻浇缁 */ + for (i = 0; i < MAX_WD_LEVEL; i++) { + wld = tmpwld[i]; + if (wld == 0) + break; + + /* 澶嶅埗宸ヤ綔璐熻浇缁勪俊鎭 */ + sret = memcpy_s(cgutil_vaddr[wld], sizeof(gscgroup_grp_t), cgutil_vaddr_back[wld], sizeof(gscgroup_grp_t)); + securec_check_errno(sret, , -1); + + if (i) /* level > 1锛屼笉鏄《绾у伐浣滆礋杞 */ + cgutil_vaddr[tmpcls]->ginfo.cls.rempct -= cgutil_vaddr[wld]->ginfo.wd.percent; + + /* 鍒涘缓宸ヤ綔璐熻浇cgroup */ + if (-1 == cgexec_create_workload_cgroup(cgutil_vaddr[wld])) { + cgconf_reset_workload_group(wld); + return -1; + } + } + + /* 鍒涘缓鏃堕棿鍏变韩cgroup */ + if (-1 == cgexec_create_timeshare_cgroup(cgutil_vaddr[tmpcls])) + return -1; + + return 0; + } + /* + * @Description: 鎭㈠澶辫触鍙戠敓鏃剁殑鏈鍚庝竴缁勬暟鎹 + * @Return: + * -1: 寮傚父 0: 姝e父 + * @See also: + */ + /*鍑芥暟鍔熻兘锛氳鍑芥暟鐢ㄤ簬鍦ㄥ彂鐢熷け璐ユ椂鎭㈠鍒嗙被缁勫拰宸ヤ綔璐熻浇缁勭殑鏈鍚庝竴缁勬暟鎹傚嚱鏁颁細妫鏌ュ浠芥枃浠朵腑鐨勬暟鎹笌褰撳墠鍐呭瓨涓殑鏁版嵁鏄惁涓鑷达紝鑻ヤ笉涓鑷村垯鏍规嵁涓嶅悓鎯呭喌杩涜鐩稿簲鐨勫鐞嗭紝鍖呮嫭鍒犻櫎缁勩佸姩鎬佹洿鏂扮瓑鎿嶄綔銆傛渶鍚庯紝灏嗘仮澶嶇殑鏁版嵁鍐欏洖閰嶇疆鏂囦欢銆 + 鍑芥暟鍙橀噺锛 + - vaddr锛氬浠芥枃浠剁殑鏄犲皠鍦板潃 + - cglen锛氶渶瑕佺殑鍐呭瓨绌洪棿澶у皬 + - cls_add锛氬緟娣诲姞鐨勫垎绫荤粍ID + - cls_del锛氬緟鍒犻櫎鐨勫垎绫荤粍ID + - wd_del锛氬緟鍒犻櫎鐨勫伐浣滆礋杞界粍ID + - clspct_update锛氬緟鍔ㄦ佹洿鏂扮殑鍒嗙被缁処D锛堟寜鐧惧垎姣旓級 + - wdpct_update锛氬緟鍔ㄦ佹洿鏂扮殑宸ヤ綔璐熻浇缁処D锛堟寜鐧惧垎姣旓級 + - quota_update锛氬緟鍔ㄦ佹洿鏂扮殑缁処D锛堟寜閰嶉锛 + - other_update锛氬緟鍔ㄦ佹洿鏂扮殑缁処D锛堝叾浠栨儏鍐碉級 + - wd_add[MAX_WD_LEVEL]锛氬緟娣诲姞鐨勫伐浣滆礋杞界粍ID鏁扮粍 + - j锛氬伐浣滆礋杞界粍ID鏁扮粍鐨勭储寮 + - sret锛氶敊璇爜 + + 鍑芥暟搴旂敤瀹炰緥锛氳繖娈典唬鐮佹槸涓涓郴缁熺鐞嗗伐鍏蜂腑鐢ㄤ簬鎭㈠缁勬暟鎹殑鍑芥暟銆備緥濡傦紝鍦ㄧ郴缁熷崌绾ц繃绋嬩腑锛屽彲鑳界敱浜庡け璐ョ瓑鍘熷洜瀵艰嚧閰嶇疆鏂囦欢琚崯鍧忥紝姝ゆ椂鍙互浣跨敤璇ュ嚱鏁板皢澶囦唤鏂囦欢涓殑鏁版嵁鎭㈠鍒板唴瀛樹腑锛屼互淇濊瘉绯荤粺姝e父杩愯銆*/ + int cgexec_recover_groups(void) + { + void* vaddr = NULL; // 澶囦唤鏂囦欢鐨勬槧灏勫湴鍧 + size_t cglen = GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t); // 璁$畻闇瑕佺殑鍐呭瓨绌洪棿澶у皬 + int cls_add = 0, cls_del = 0, wd_del = 0; // 鍒嗙被缁勭浉鍏崇殑鍙橀噺 + int clspct_update = 0, wdpct_update = 0, quota_update = 0, other_update = 0; // 鍔ㄦ佹洿鏂扮浉鍏崇殑鍙橀噺 + int wd_add[MAX_WD_LEVEL] = { 0 }; // 宸ヤ綔璐熻浇缁勭浉鍏崇殑鍙橀噺 + int j = 0; // 璁℃暟鍣 + errno_t sret; // 閿欒鐮 + + /* 閲嶇疆鏁扮粍 */ + sret = memset_s(wd_add, sizeof(wd_add), 0, sizeof(wd_add)); + securec_check_errno(sret, , -1); + + /* 鑾峰彇澶囦唤鏂囦欢鐨勬槧灏勫湴鍧 */ + vaddr = cgconf_map_backup_conffile(false); + if (NULL == vaddr) + return -1; + + for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { + cgutil_vaddr_back[i] = (gscgroup_grp_t*)vaddr + i; + + /* 鏌ユ壘涓嶅悓鐨勭粍鏉$洰鍐呭瓨鍖哄煙 */ + if ((i >= CLASSCG_START_ID && i <= WDCG_END_ID) && + (0 != memcmp(cgutil_vaddr[i], cgutil_vaddr_back[i], sizeof(gscgroup_grp_t)))) { + if (i >= CLASSCG_START_ID && i <= CLASSCG_END_ID) { + if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used == 0) + cls_del = i; + else if (cgutil_vaddr[i]->used == 0 && cgutil_vaddr_back[i]->used) + cls_add = i; + else if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used && + cgexec_check_update_groups(cgutil_vaddr[i], cgutil_vaddr_back[i])) { + /* 鍔ㄦ佹洿鏂 */ + if (cgutil_vaddr[i]->ginfo.cls.percent != cgutil_vaddr_back[i]->ginfo.cls.percent) + clspct_update = i; + else if (cgutil_vaddr[i]->ainfo.quota != cgutil_vaddr_back[i]->ainfo.quota) + quota_update = i; + else + other_update = i; + } + } + else if (i > WDCG_START_ID) { + if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used == 0) + wd_del = i; + else if (cgutil_vaddr[i]->used == 0 && cgutil_vaddr_back[i]->used) { + if (j == MAX_WD_LEVEL) { + fprintf(stderr, "ERROR: configure file has more than %d different workload!\n", MAX_WD_LEVEL); + goto error; + } + wd_add[j++] = i; + } + else if (cgutil_vaddr[i]->used && cgutil_vaddr_back[i]->used && + cgexec_check_update_groups(cgutil_vaddr[i], cgutil_vaddr_back[i])) { + /* 鍔ㄦ佹洿鏂 */ + if (cgutil_vaddr[i]->ginfo.wd.percent != cgutil_vaddr_back[i]->ginfo.wd.percent) + wdpct_update = i; + else if (cgutil_vaddr[i]->ainfo.quota != cgutil_vaddr_back[i]->ainfo.quota) { + if (quota_update) { + fprintf(stderr, + "ERROR: cpu core quota has been set on %d, " + "it should not be appear on %d again!\n", + quota_update, + i); + goto error; + } + quota_update = i; + } + else + other_update = i; + } + } + } + } + + /* -u class update */ + if (clspct_update && -1 == cgexec_recover_update_percent_groups(clspct_update)) + goto error; + + /* -u workload update */ + if (wdpct_update && -1 == cgexec_recover_update_percent_groups(wdpct_update)) + goto error; + + /* -u --fixed update */ + if (quota_update && -1 == cgexec_recover_update_quota_groups(quota_update)) + goto error; + + /* like blkio throttle update */ + if (clspct_update == 0 && wdpct_update == 0 && quota_update == 0 && other_update && + (-1 == cgexec_update_cgroup_value(cgutil_vaddr_back[other_update]))) + goto error; + + /* 鐩存帴鍒犻櫎鍒嗙被缁 */ + if (cls_del) { + if (-1 == cgexec_delete_default_cgroup(cgutil_vaddr[cls_del])) + goto error; + cgconf_reset_class_group(cls_del); + } + else if (cls_del == 0 && wd_del) /* 鍒犻櫎宸ヤ綔璐熻浇缁 */ + { + (void)cgexec_delete_workload_cgroup(cgutil_vaddr[wd_del]); + } + + /* 娣诲姞鍒嗙被缁勫拰宸ヤ綔璐熻浇缁 */ + if (wd_add[0]) { + if (-1 == cgexec_recover_create_groups(cls_add, wd_add)) + goto error; + } + + /* 鏈鍚庢仮澶嶉厤缃枃浠 */ + sret = memcpy_s(cgutil_vaddr[0], cglen, vaddr, cglen); + securec_check_errno(sret, (void)munmap(vaddr, cglen); cgconf_remove_backup_conffile(); , -1); + + (void)munmap(vaddr, cglen); + cgconf_remove_backup_conffile(); + return 0; + + error: + securec_check_errno(sret, (void)munmap(vaddr, cglen); , -1); + cgconf_remove_backup_conffile(); + return -1; + } + /* + * @Description: 鎸傝浇鎺у埗缁勩 + * @IN : void + * @Return: void + * @See also: + */ + void cgexec_mount_cgroups(void) + { + /* 鎸傝浇鎺у埗缁 */ + (void)cgexec_mount_root_cgroup(); + } + + /* + * @Description: 鍗歌浇鎺у埗缁勩 + * @IN : void + * @Return: void + * @See also: + */ + void cgexec_umount_cgroups(void) + { + /* 鍗歌浇鎺у埗缁 */ + (void)cgexec_umount_root_cgroup(); + } + + /* + * function name: cgexec_create_cm_default_cgroup + * description : 鍒涘缓cm榛樿鎺у埗缁 + * arguments : void + * return value : + * -1: 寮傚父 + * 0: 姝e父 + * Note: 璇ュ嚱鏁扮敤浜庡垱寤烘柊鐨勬帶鍒剁粍銆 + */ + + int cgexec_create_cm_default_cgroup(void) + { + int ret = 0; + errno_t rc = EOK; + char cgpath[GPNAME_PATH_LEN] = { 0 }; + struct stat buf; + + rc = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); + securec_check_errno(rc, , -1); + rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s/%s:%s", cgutil_opt.mpoints[0], GSCGROUP_CM, cgutil_opt.user); + securec_check_ss_c(rc, "\0", "\0"); + + // 鍒涘缓cm鎺у埗缁勬椂蹇呴』浠oot鐢ㄦ埛杩愯銆 + if (geteuid() != 0) + return 0; + + if (0 == stat(cgpath, &buf)) { + fprintf(stderr, "'%s' 宸插瓨鍦紝蹇界暐鍒涘缓姝ゆ帶鍒剁粍銆俓n", cgpath); + return 0; + } + + rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s:%s", GSCGROUP_CM, cgutil_opt.user); + securec_check_ss_c(rc, "\0", "\0"); + + ret = cgexec_create_default_cgroup(cgpath, DEFAULT_CM_CPUSHARES, DEFAULT_IO_WEIGHT, cgutil_allset); + if (ret == -1) { + fprintf(stderr, "鏃犳硶鍒涘缓cm鎺у埗缁勶紝cgpath涓%s銆俓n", cgpath); + return -1; + } + + return 0; + } + + /* 鍒犻櫎cm鎺у埗缁 */ + int cgexec_delete_cm_cgroup(void) + { + int ret = 0; + errno_t rc = EOK; + char cgpath[GPNAME_PATH_LEN] = { 0 }; + struct stat buf; + + rc = memset_s(&buf, sizeof(buf), 0, sizeof(buf)); + securec_check_errno(rc, , -1); + rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s/%s:%s", cgutil_opt.mpoints[0], GSCGROUP_CM, cgutil_opt.user); + securec_check_ss_c(rc, "\0", "\0"); + + if (0 != stat(cgpath, &buf)) { + return -1; + } + + rc = sprintf_s(cgpath, GPNAME_PATH_LEN, "%s:%s", GSCGROUP_CM, cgutil_opt.user); + securec_check_ss_c(rc, "\0", "\0"); + + ret = cgexec_delete_cgroups(cgpath); + if (ret == -1) { + fprintf(stderr, "鏃犳硶鍒犻櫎cm鎺у埗缁勶紝cgpath涓%s銆俓n", cgpath); + return -1; + } + + return 0; + } + -- 2.34.1 From 092c13012c7c2af964871f7e327c3b419afbce13 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:02:29 +0800 Subject: [PATCH 07/56] Delete 'src/bin/gs_cgroup/cgptree.cpp' --- src/bin/gs_cgroup/cgptree.cpp | 1008 --------------------------------- 1 file changed, 1008 deletions(-) delete mode 100644 src/bin/gs_cgroup/cgptree.cpp diff --git a/src/bin/gs_cgroup/cgptree.cpp b/src/bin/gs_cgroup/cgptree.cpp deleted file mode 100644 index 81ad6fdb8..000000000 --- a/src/bin/gs_cgroup/cgptree.cpp +++ /dev/null @@ -1,1008 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - *------------------------------------------------------------------------- - * - * cgptree.cpp - * Display the Cgroup Tree structure - * - * IDENTIFICATION - * src/bin/gs_cgroup/cgptree.cpp - * - *------------------------------------------------------------------------- - */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "cgutil.h" - -/* data structure of controller information */ -struct controller_info { - char* ctrl_name; /* controller name */ - char* mount_point; /* the directory path of mount point */ - struct group_info* group_head; /* point to the head of group info structure */ - struct controller_info* next; /* point to the next controller */ -}; - -/* data structure of group information */ -struct group_info { - char* grpname; /* the group name */ - char* relpath; /* the relative path of this group */ - int depth; /* the depth of this group */ - int64_t cpu_quota; /* the value of cpu.cfs_quota_us */ - int64_t cpu_period; /* the value of cpu.cfs_period_us */ - u_int64_t cpu_shares; /* the value of cpu.shares */ - u_int64_t blkio_weight; /* the value of blkio.weight */ - char* blkio_bpsread; /* io bps read value */ - char* blkio_iopsread; /* io iops read value */ - char* blkio_bpswrite; /* io bps write value */ - char* blkio_iopswrite; /* io iops write value */ - char* cpuset_cpus; /* cpuset value */ - int64_t cpuset_mems; /* cpuset memory info */ - int64_t cpuacct_usage; /* cpu account */ - struct task_info* task_head; /* the task list in this group */ - struct group_info* parent; /* point to the parent group (upper level) */ - struct group_info* child_head; /* point to the child group (lower level) */ - struct group_info* prev; /* point to the previous group (same level) */ - struct group_info* next; /* point to the next group (same level) */ -}; - -/* data structure of task information */ -struct task_info { - pid_t pid; /* the thread id (gettid) */ - struct task_info* next; /* point to the next task */ -}; - -extern char* cgutil_subsys_table[]; - -#ifdef ENABLE_UT -#define static -#endif - -#define CHECK_GSCGROUP_TOP_DATABASE \ - ((strncmp(rel_path, GSCGROUP_TOP_DATABASE ":", sizeof(GSCGROUP_TOP_DATABASE)) == 0) && \ - (0 == strncmp(tmpstr, cgutil_passwd_user->pw_name, tmplen)) && \ - ('\0' == *(tmpstr + tmplen) || '/' == *(tmpstr + tmplen))) - -#define CHECK_GSCGROUP_CM \ - ((strncmp(rel_path, GSCGROUP_CM ":", sizeof(GSCGROUP_CM)) == 0) && \ - (0 == strncmp(cm_tmpstr, cgutil_passwd_user->pw_name, tmplen)) && \ - ('\0' == *(cm_tmpstr + tmplen) || '/' == *(cm_tmpstr + tmplen))) - -/* - ***************** STATIC FUNCTIONS ************************ - */ - -/* - * function name: cgpstree_free_task_list - * description : free task structure list - * arguments : - * head: the data structure of task list information - */ -static void cgpstree_free_task_list(struct task_info* head) -{ - struct task_info* curr = head; - struct task_info* next = NULL; - - while (curr != NULL) { - next = curr->next; - - free(curr); - curr = NULL; - - if (next == NULL) - return; - - curr = next; - } -} - -/* - * function name: cgptree_rec_free_group_tree - * description : free group structure recursively - * arguments : - * current: the data structure of group tree information - */ -static void cgptree_rec_free_group_tree(struct group_info* current) -{ - if (current == NULL) - return; - - if (current->next != NULL) - cgptree_rec_free_group_tree(current->next); - if (current->child_head != NULL) - cgptree_rec_free_group_tree(current->child_head); - - if (current->grpname != NULL) - free(current->grpname); - - if (current->relpath != NULL) - free(current->relpath); - - if (current->blkio_bpsread != NULL) - free(current->blkio_bpsread); - - if (current->blkio_iopsread != NULL) - free(current->blkio_iopsread); - - if (current->blkio_bpswrite != NULL) - free(current->blkio_bpswrite); - - if (current->blkio_iopswrite != NULL) - free(current->blkio_iopswrite); - - if (current->cpuset_cpus != NULL) - free(current->cpuset_cpus); - - if (current->task_head != NULL) - cgpstree_free_task_list(current->task_head); - - free(current); - current = NULL; - - return; -} - -/* - * function name: cgptree_free_group_tree - * description : free group structure of one controller - * arguments : - * head: the head group of one controller - */ -static void cgptree_free_group_tree(struct group_info* head) -{ - cgptree_rec_free_group_tree(head); - - return; -} - -/* - * function name: cgptree_get_cgroup - * description : get the cgroup structure based on the relative path - * arguments : - * relpath: the relative path - * return value : the pointer of cgroup structure - * - */ -static struct cgroup* cgptree_get_cgroup(const char* relpath) -{ - int ret = 0; - struct cgroup* cg = NULL; - - /* allocate new cgroup structure */ - cg = cgroup_new_cgroup(relpath); - if (cg == NULL) { - ret = ECGFAIL; - fprintf(stdout, "failed to create the new cgroup for %s\n", cgroup_strerror(ret)); - return NULL; - } - - /* get all information regarding the cgroup from kernel */ - ret = cgroup_get_cgroup(cg); - if (ret != 0) { - fprintf(stdout, "failed to get cgroup information for %s(%d)\n", cgroup_strerror(ret), ret); - cgroup_free(&cg); - return NULL; - } - - return cg; -} - -/* - * function name: cgpstree_get_task_list - * description : get the task list based on the relative path and mount name - * arguments : - * rel_path: the relative path of one group - * ctl: the name of mount information - */ -static struct task_info* cgpstree_get_task_list(const char* rel_path, const char* ctrl) -{ - void* task_handle = NULL; - pid_t pid; - int error; - struct task_info* tinfo_head = NULL; - struct task_info* curr_tinfo = NULL; - struct task_info* prev_tinfo = NULL; - - error = cgroup_get_task_begin(rel_path, ctrl, &task_handle, &pid); - - if (error && error != ECGEOF) - return NULL; - - while (error != ECGEOF) { - curr_tinfo = (struct task_info*)calloc(1, sizeof(struct task_info)); - - if (curr_tinfo == NULL) { - cgpstree_free_task_list(tinfo_head); - cgroup_get_task_end(&task_handle); - return NULL; - } - - if (tinfo_head == NULL) - tinfo_head = curr_tinfo; - else - prev_tinfo->next = curr_tinfo; - - curr_tinfo->pid = pid; - - curr_tinfo->next = NULL; - - error = cgroup_get_task_next(&task_handle, &pid); - - if (error && error != ECGEOF) { - cgpstree_free_task_list(tinfo_head); - cgroup_get_task_end(&task_handle); - return NULL; - } - - prev_tinfo = curr_tinfo; - } - - cgroup_get_task_end(&task_handle); - return tinfo_head; -} - -/* error report */ -#define ERROR_REPORT(error, strings, grpname) \ - { \ - if (error) { \ - if (level <= *min_level || (*min_level == 0)) { \ - fprintf(stdout, \ - "NOTICE: Cgroup get %s failed for %s group: %s\n", \ - strings, \ - grpname, \ - cgroup_strerror(error)); \ - *min_level = level; \ - } \ - } \ - } - -/* - * function name: cgptree_get_group_info - * description : get the group info for specified mount point and group info curr_ginfo - * arguments : - * curr_ginfo: group info to be updated. - * mount_info: specified mount info, like blkio, cpu, cpuset. - * min_level: IN@OUT, get the error report min_level - */ -void cgptree_get_group_info(struct group_info* curr_ginfo, const struct cgroup_mount_point& mount_info, int* min_level) -{ - int error; - int level = 0; - - struct cgroup* cg = NULL; - struct cgroup_controller* cgc = NULL; - - char* rel_path = curr_ginfo->relpath; - - if (rel_path == NULL) - return; - - cg = cgptree_get_cgroup(rel_path); - - /* error report has been done in cgptree_get_cgroup */ - if (cg == NULL) - return; - - level = curr_ginfo->depth; - - /* get controller */ - cgc = cgroup_get_controller(cg, mount_info.name); - if (cgc == NULL) { - /* - * only the lowest levels of wrong case need error report. - * eg: if Class is not available, then no need to report DefaultClass's - * error message. - * when error occurs for Class, the scan of the tree doesn't - * pause until "Backend" is checked. - */ - if (*min_level == 0 || (level <= *min_level)) { - fprintf(stderr, - "Notice: Cgroup add_controller for group \"%s\" of mount point \"%s\" failed\n", - curr_ginfo->grpname, - mount_info.name); - - *min_level = level; - } - - cgroup_free(&cg); - cgroup_free_controllers(cg); - return; - } - - /* get values of cpu.shares */ - if (0 == strcmp(mount_info.name, MOUNT_CPU_NAME)) { - error = cgroup_get_value_uint64(cgc, CPU_SHARES, &(curr_ginfo->cpu_shares)); - ERROR_REPORT(error, CPU_SHARES, curr_ginfo->grpname); - } - /* get values of cpuset.cpus */ - if (0 == strcmp(mount_info.name, MOUNT_CPUSET_NAME)) { - error = cgroup_get_value_string(cgc, CPUSET_CPUS, &(curr_ginfo->cpuset_cpus)); - ERROR_REPORT(error, CPUSET_CPUS, curr_ginfo->grpname); - } - /* get values of cpuacct.cpus */ - if (0 == strcmp(mount_info.name, MOUNT_CPUACCT_NAME)) { - error = cgroup_get_value_int64(cgc, CPUACCT_USAGE, &(curr_ginfo->cpuacct_usage)); - ERROR_REPORT(error, CPUACCT_USAGE, curr_ginfo->grpname); - } - - cgroup_free_controllers(cg); - cgroup_free(&cg); - return; -} - -/* - * function name: cgptree_get_group_tree - * description : build the tree for the first time, scan the cgroup file. - * arguments : - * mount_info: specified mount info, like blkio, cpu, cpuset. - * return : - * ginfo: the root group info of the built group_info tree - */ -static struct group_info* cgptree_get_group_tree(const struct cgroup_mount_point& mount_info) -{ - int curr_depth = -1; - int prev_depth = -1; - void* tree_handle = NULL; - int level = 0; - int error; - size_t tmplen = 0; - char* root_path = NULL; - char *rel_path = NULL, *tmpstr = NULL, *cm_tmpstr = NULL; - int min_level = 0; - - struct cgroup_file_info info; - struct group_info* curr_ginfo = NULL; - struct group_info* prev_ginfo = NULL; - struct group_info* root_ginfo = NULL; - - /* begin to walk through the group tree */ - error = cgroup_walk_tree_begin(mount_info.name, "/", 0, &tree_handle, &info, &level); - if (error && error != ECGEOF) - return NULL; - - /* save the path of mount point */ - root_path = strdup(info.full_path); - if (root_path == NULL) { - cgroup_walk_tree_end(&tree_handle); - return NULL; - } - - error = cgroup_walk_tree_set_flags(&tree_handle, CGROUP_WALK_TYPE_PRE_DIR); - if (error) { - free(root_path); - root_path = NULL; - cgroup_walk_tree_end(&tree_handle); - return NULL; - } - - while (error != ECGEOF) { - /* get the relative path */ - rel_path = (char*)(info.full_path + strlen(root_path)); - - tmpstr = rel_path + sizeof(GSCGROUP_TOP_DATABASE); - cm_tmpstr = rel_path + sizeof(GSCGROUP_CM); - tmplen = strlen(cgutil_passwd_user->pw_name); - - if ((CHECK_GSCGROUP_TOP_DATABASE || CHECK_GSCGROUP_CM) && info.type == CGROUP_FILE_TYPE_DIR) { - curr_ginfo = (struct group_info*)calloc(1, sizeof(struct group_info)); - - if (curr_ginfo == NULL) - goto error; - - curr_ginfo->depth = info.depth; - curr_ginfo->grpname = strdup(info.path); - - if (curr_ginfo->grpname == NULL) { - free(curr_ginfo); - curr_ginfo = NULL; - goto error; - } - - curr_ginfo->relpath = strdup(rel_path); - - if (curr_ginfo->relpath == NULL) { - free(curr_ginfo->grpname); - curr_ginfo->grpname = NULL; - free(curr_ginfo); - curr_ginfo = NULL; - goto error; - } - - /* get the task list in this group */ - curr_ginfo->task_head = cgpstree_get_task_list(rel_path, mount_info.name); - - cgptree_get_group_info(curr_ginfo, mount_info, &min_level); - - curr_depth = info.depth; - - if (root_ginfo == NULL) { - root_ginfo = curr_ginfo; - } else if (prev_depth == curr_depth) { - prev_ginfo->next = curr_ginfo; - curr_ginfo->prev = prev_ginfo; - curr_ginfo->parent = prev_ginfo->parent; - } else if ((prev_depth + 1) == curr_depth) { - prev_ginfo->child_head = curr_ginfo; - curr_ginfo->parent = prev_ginfo; - } else { /* must jump when if current is for prev neither child nor sibling */ - while (true) { - if (curr_ginfo->depth == prev_ginfo->depth) - break; - prev_ginfo = prev_ginfo->parent; - continue; - } - - /** prev_ginfo is sibling here to follow **/ - prev_ginfo->next = curr_ginfo; - curr_ginfo->prev = curr_ginfo; - curr_ginfo->parent = prev_ginfo->parent; - } - - prev_ginfo = curr_ginfo; - prev_depth = prev_ginfo->depth; - } - - error = cgroup_walk_tree_next(0, &tree_handle, &info, level); - if (error && error != ECGEOF) { - /* free resource when error */ - goto error; - } - } - - free(root_path); - root_path = NULL; - cgroup_walk_tree_end(&tree_handle); - - return root_ginfo; - -error: - free(root_path); - root_path = NULL; - cgroup_walk_tree_end(&tree_handle); - - if (root_ginfo != NULL) - cgptree_free_group_tree(root_ginfo); - - return NULL; -} - -/* - * function name: cgptree_walk_group - * description : get cgroup info with linked list. - */ -static struct group_info* cgptree_walk_group(struct group_info* ginfo) -{ - if (ginfo->child_head != NULL) - return ginfo->child_head; - - if (ginfo->next != NULL) - return ginfo->next; - - while (ginfo->parent != NULL) { - ginfo = ginfo->parent; - - if (ginfo->next != NULL) - return ginfo->next; - } - return NULL; -} - -/* - * function name: cgptree_get_tree_info - * description : scan the tree, and get the information of the mount_info - * : from the cgroup file system - * arguments : - * mount_info: specified mount info, like blkio, cpu, cpuset. - * return : - * root_ginfo: the root group info of the built group_info tree - */ -struct group_info* cgptree_get_tree_info(const cgroup_mount_point& mount_info, struct group_info* root_ginfo) -{ - struct group_info* curr_ginfo = root_ginfo; - int min_level = 0; - - while (curr_ginfo != NULL) { - char* rel_path = curr_ginfo->relpath; - - if (rel_path == NULL) { - curr_ginfo = cgptree_walk_group(curr_ginfo); - continue; - } - - /* get the task list in this group */ - cgptree_get_group_info(curr_ginfo, mount_info, &min_level); - - curr_ginfo = cgptree_walk_group(curr_ginfo); - } - - return root_ginfo; -} - -/* - * function name: cgptree_get_cgroup_new - * description : scan the mount controller, and get the group_info of the tree, - * : fill the different subsys information - * : in the same group_info tree - * return : - * ginfo: the root group info of the fully built group_info tree - */ -static struct group_info* cgptree_get_cgroup_new() -{ - int error = 0; - void* ctrl_handle = NULL; - int count = 0; - - struct cgroup_mount_point info = {{0}, {0}}; - struct group_info* ginfo = NULL; - - error = cgroup_get_controller_begin(&ctrl_handle, &info); - if (error) { - fprintf(stderr, "get controller begin failed: %s\n", cgroup_strerror(error)); - return NULL; - } - - while (error != ECGEOF) { - - if (*info.name && strcmp(info.name, MOUNT_CPU_NAME) != 0 && strcmp(info.name, MOUNT_CPUSET_NAME) != 0 && - strcmp(info.name, MOUNT_CPUACCT_NAME) != 0) { - error = cgroup_get_controller_next(&ctrl_handle, &info); - if (error && error != ECGEOF) { - fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); - break; - } - continue; - } - - /* for new_allocate tree, get the tree_node info from the first scanned subsys mount_info */ - if (!count) { - ginfo = cgptree_get_group_tree(info); - } else { - /* the following subsys group info will be filled in the already built tree */ - ginfo = cgptree_get_tree_info(info, ginfo); - } - - /* - * get the group tree of this controller - * if the group is not valid, skip it - */ - if (ginfo == NULL) { - fprintf(stderr, "Notice: get tree information for mount point %s failed\n", info.name); - - error = cgroup_get_controller_next(&ctrl_handle, &info); - if (error && error != ECGEOF) { - fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); - break; - } - continue; - } - - /* - * the first time allocate memory to build the tree - * next loop, there will be no need to build any node of the tree, - * but only fill in the node info of the tree. - */ - count++; - - error = cgroup_get_controller_next(&ctrl_handle, &info); - if (error && error != ECGEOF) { - fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); - break; - } - } - - (void)cgroup_get_controller_end(&ctrl_handle); - - return ginfo; -} -/* - * function name: cgptree_print_space - * description : print the space based on level - * - */ -static void cgptree_print_space(int level) -{ - while (level-- > 0) - printf("\t"); -} - -/* - * function name: cgptree_delete_group - * description : delete the cgroup recursively - */ -static void cgptree_delete_group(struct group_info* ginfo) -{ - if (NULL == ginfo) - return; - - if (ginfo->child_head != NULL) - cgptree_delete_group(ginfo->child_head); - - if (ginfo->next != NULL) - cgptree_delete_group(ginfo->next); - - cgexec_delete_cgroups(ginfo->relpath); -} - -/* - * function name: cgptree_print_group - * description : print the group information as tree style - * arguments : - * ctlrname: the name of the controller - * ginfo: the head of group information - * level: the level of the group - * - */ -#define BLKIO_STR_UPDATE(s) \ - { \ - char* q = NULL; \ - do { \ - q = strchr(s, '\n'); \ - if (q != NULL) \ - *q = ';'; \ - } while (q != NULL); \ - } - -/* - * @Description: print cgroup info in tree. - * @IN ginfo: group info - * @IN level: group level - * @Return: void - * @See also: - */ -static void cgptree_print_group_new(struct group_info* ginfo, int level) -{ - struct task_info* tinfo = ginfo->task_head; - int cnt = 0; - - cgptree_print_space(level); - - /* print group name */ - fprintf(stdout, "- %s ", ginfo->grpname); - - /* print cpu shares */ - fprintf(stdout, "(shares: %lu,", ginfo->cpu_shares); - - /* print cpuset */ - fprintf(stdout, " cpus: %s", ginfo->cpuset_cpus); - - if (cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) { - fprintf(stdout, ", weight: %lu", ginfo->blkio_weight); - - if ((ginfo->blkio_bpsread != NULL) && ginfo->blkio_bpsread[0]) { - BLKIO_STR_UPDATE(ginfo->blkio_bpsread); - fprintf(stdout, ", bpsread: \"%s\"", ginfo->blkio_bpsread); - } - if ((ginfo->blkio_iopsread != NULL) && ginfo->blkio_iopsread[0]) { - BLKIO_STR_UPDATE(ginfo->blkio_iopsread); - fprintf(stdout, ", iopsread: \"%s\"", ginfo->blkio_iopsread); - } - if ((ginfo->blkio_bpswrite != NULL) && ginfo->blkio_bpswrite[0]) { - BLKIO_STR_UPDATE(ginfo->blkio_bpswrite); - fprintf(stdout, ", bpswrite: \"%s\"", ginfo->blkio_bpswrite); - } - if ((ginfo->blkio_iopswrite != NULL) && ginfo->blkio_iopswrite[0]) { - BLKIO_STR_UPDATE(ginfo->blkio_iopswrite); - fprintf(stdout, ", iopswrite: \"%s\"", ginfo->blkio_iopswrite); - } - } - - fprintf(stdout, ")\n"); - - if (tinfo != NULL) - cgptree_print_space(level + 1); - - /* print thread id in the group */ - while (tinfo != NULL) { - fprintf(stdout, "%8d ", (int)tinfo->pid); - - if (0 == (++cnt) % 5) { - fprintf(stdout, "\n"); - cgptree_print_space(level + 1); - } - - tinfo = tinfo->next; - } - - if (cnt) - fprintf(stdout, "\n"); - - fflush(stdout); - - /* print next one */ - if (ginfo->child_head != NULL) - cgptree_print_group_new(ginfo->child_head, level + 1); - - if (ginfo->next != NULL) - cgptree_print_group_new(ginfo->next, level); -} - -/* - * function name: cgptree_free - * description : free the controller information - * arguments : - * cinfo_head: the head of controller information - * - */ -static void cgptree_free(struct controller_info* cinfo_head) -{ - struct controller_info* curr = cinfo_head; - struct controller_info* next = NULL; - - if (NULL == curr) - return; - - /* free controller data structure */ - while (curr != NULL) { - next = curr->next; - - if (curr->ctrl_name != NULL) - free(curr->ctrl_name); - if (curr->mount_point != NULL) - free(curr->mount_point); - - cgptree_free_group_tree(curr->group_head); - - free(curr); - curr = NULL; - curr = next; - } -} - -void free_controller_list_resource(struct controller_info* curr_cinfo, - char* curr_path, - struct controller_info* cinfo_head, - void* ctrl_handle) -{ - if (curr_cinfo != NULL) { - if (curr_cinfo->ctrl_name != NULL) { - free(curr_cinfo->ctrl_name); - } - free(curr_cinfo); - } - if (curr_path != NULL) { - free(curr_path); - } - cgptree_free(cinfo_head); - cgroup_get_controller_end(&ctrl_handle); -} - -/* - * function name: cgptree_get_controller_list - * description : read all cgroups and make up of the controller information - * return value : the head of controller information - * - */ -static struct controller_info* cgptree_get_controller_list(void) -{ - int error = 0; - char* curr_path = NULL; - void* ctrl_handle = NULL; - struct cgroup_mount_point info = {{0}, {0}}; - struct controller_info* cinfo_head = NULL; - struct controller_info* curr_cinfo = NULL; - struct controller_info* prev_cinfo = NULL; - struct group_info* ginfo = NULL; - - error = cgroup_get_controller_begin(&ctrl_handle, &info); - if (error) { - fprintf(stderr, "get controller begin failed: %s\n", cgroup_strerror(error)); - return NULL; - } - - while (error != ECGEOF) { - curr_path = strdup(info.path); - if (curr_path == NULL) { - free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); - return NULL; - } - - /* - * get the group tree of this controller - * if the group is not valid, skip it - */ - ginfo = cgptree_get_group_tree(info); - if (NULL == ginfo) { - free(curr_path); - - error = cgroup_get_controller_next(&ctrl_handle, &info); - if (error && error != ECGEOF) { - fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); - break; - } - - continue; - } - - /* allocate structure for new controller */ - curr_cinfo = (struct controller_info*)calloc(1, sizeof(struct controller_info)); - if (curr_cinfo == NULL) { - free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); - return NULL; - } - - curr_cinfo->ctrl_name = strdup(info.name); - if (curr_cinfo->ctrl_name == NULL) { - free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); - return NULL; - } - curr_cinfo->mount_point = strdup(info.path); - if (curr_cinfo->mount_point == NULL) { - free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); - return NULL; - } - - /* get the group tree of this controller */ - curr_cinfo->group_head = ginfo; - - error = cgroup_get_controller_next(&ctrl_handle, &info); - if (error && error != ECGEOF) { - fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); - if (curr_cinfo->ctrl_name != NULL) - free(curr_cinfo->ctrl_name); - if (curr_cinfo->mount_point != NULL) - free(curr_cinfo->mount_point); - cgptree_free_group_tree(curr_cinfo->group_head); - free(curr_cinfo); - break; - } - - if (cinfo_head == NULL) - cinfo_head = curr_cinfo; - else - prev_cinfo->next = curr_cinfo; - - prev_cinfo = curr_cinfo; - if (curr_path != NULL) { - free(curr_path); - } - curr_cinfo = NULL; - } - - cgroup_get_controller_end(&ctrl_handle); - - return cinfo_head; -} -/* - **************** EXTERNAL FUNCTION ******************************** - */ - -/* - * function name: cgptree_display_cgroups - * description : display the Cgroup tree information - * return value : - * -1: abnormal - * 0: normal - */ -int cgptree_display_cgroups(void) -{ - struct group_info* ginfo_new = NULL; - - ginfo_new = cgptree_get_cgroup_new(); - if (ginfo_new == NULL) { - /* release group info */ - fprintf(stderr, "failed to get the new cgroup tree information!\n"); - return -1; - } - - /* print current all mount points */ - for (int i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { - if (i == MOUNT_BLKIO_ID) - continue; - - if (i == 0) - fprintf(stdout, "Mount Information:\n"); - - fprintf(stdout, "%s:%s\n", cgutil_subsys_table[i], cgutil_opt.mpoints[i]); - } - - fprintf(stdout, "\nGroup Tree Information:\n"); - - /* print group info */ - cgptree_print_group_new(ginfo_new, 0); - - cgptree_rec_free_group_tree(ginfo_new); - - return 0; -} -/* - * function name: cgptree_drop_cgroups - * description : drop all users' cgroup - * return value : - * -1: abnormal - * 0: normal - */ -int cgptree_drop_cgroups(void) -{ - struct controller_info* cinfo_head = NULL; - - cinfo_head = cgptree_get_controller_list(); - if (cinfo_head == NULL) { - fprintf(stderr, "failed to get Cgroup tree information!\n"); - return -1; - } - - cgptree_delete_group(cinfo_head->group_head); - - cgptree_free(cinfo_head); - - return 0; -} - -/* - * function name: cgptree_drop_nodegroup_cgroups - * description : drop all users' nodegroup cgroup - * return value : - * -1: abnormal - * 0: normal - */ -int cgptree_drop_nodegroup_cgroups(const char* name) -{ - struct controller_info* cinfo_head = NULL; - - cinfo_head = cgptree_get_controller_list(); - if (cinfo_head == NULL) { - fprintf(stderr, "failed to get Cgroup tree information!\n"); - return -1; - } - - struct group_info* ginfo = cinfo_head->group_head->child_head; - while (ginfo != NULL) { - if (strcmp(ginfo->grpname, name) == 0) { - break; - } - - ginfo = ginfo->next; - } - - if (ginfo != NULL) { - if (ginfo->child_head != NULL) - cgptree_delete_group(ginfo->child_head); - - cgexec_delete_cgroups(ginfo->relpath); - } else if (cinfo_head->group_head->child_head == NULL) { - fprintf(stderr, - "failed to find the cgroup (%s) with " - " controller name(%s), mount_point(%s), group_head(%s).\n", - name, - cinfo_head->ctrl_name, - cinfo_head->mount_point, - cinfo_head->group_head->grpname); - } else { - fprintf(stderr, - "failed to find the cgroup (%s) with " - " controller name(%s), mount_point(%s), " - " group_head(%s), child_head(%s).\n", - name, - cinfo_head->ctrl_name, - cinfo_head->mount_point, - cinfo_head->group_head->grpname, - cinfo_head->group_head->child_head->grpname); - } - - cgptree_free(cinfo_head); - - return 0; -} -- 2.34.1 From 1a4c7c588ad3d71c30f24f15bf8db2495c4b9204 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:02:46 +0800 Subject: [PATCH 08/56] ADD file via upload --- src/bin/gs_cgroup/cgptree.cpp | 966 ++++++++++++++++++++++++++++++++++ 1 file changed, 966 insertions(+) create mode 100644 src/bin/gs_cgroup/cgptree.cpp diff --git a/src/bin/gs_cgroup/cgptree.cpp b/src/bin/gs_cgroup/cgptree.cpp new file mode 100644 index 000000000..9d14511ab --- /dev/null +++ b/src/bin/gs_cgroup/cgptree.cpp @@ -0,0 +1,966 @@ +/* + * 版权所有 (c) 2020 华为技术有限公司。 + * + * openGauss在Mulan PSL v2下授权许可。 + * 您可以根据Mulan PSL v2的条款和条件使用本软件。 + * 您可以在以下网址获得Mulan PSL v2的副本: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * 本软件按“原样”提供,不提供任何明示或暗示的保证, + * 包括但不限于对特定用途的适销性和对非侵权的任何隐含保证。 + * 有关更多详情,请参见Mulan PSL v2。 + *------------------------------------------------------------------------- + * + * cgptree.cpp + * 显示Cgroup树结构 + * + * 识别 + * src/bin/gs_cgroup/cgptree.cpp + * + *------------------------------------------------------------------------- + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cgutil.h" + + /* 控制器信息的数据结构 */ +struct controller_info { + char* ctrl_name; /* 控制器名称 */ + char* mount_point; /* 挂载点的目录路径 */ + struct group_info* group_head; /* 指向组信息结构的头部 */ + struct controller_info* next; /* 指向下一个控制器 */ +}; + +/* 组信息的数据结构 */ +struct group_info { + char* grpname; /* 组名 */ + char* relpath; /* 相对路径 */ + int depth; /* 深度 */ + int64_t cpu_quota; /* cpu.cfs_quota_us的值 */ + int64_t cpu_period; /* cpu.cfs_period_us的值 */ + u_int64_t cpu_shares; /* cpu.shares的值 */ + u_int64_t blkio_weight; /* blkio.weight的值 */ + char* blkio_bpsread; /* io bps读取值 */ + char* blkio_iopsread; /* io iops读取值 */ + char* blkio_bpswrite; /* io bps写入值 */ + char* blkio_iopswrite; /* io iops写入值 */ + char* cpuset_cpus; /* cpuset值 */ + int64_t cpuset_mems; /* cpuset内存信息 */ + int64_t cpuacct_usage; /* cpu使用情况 */ + struct task_info* task_head; /* 组内任务列表 */ + struct group_info* parent; /* 指向上级组 */ + struct group_info* child_head; /* 指向下级组 */ + struct group_info* prev; /* 指向同级前一个组 */ + struct group_info* next; /* 指向同级后一个组 */ +}; + +/* 任务信息的数据结构 */ +struct task_info { + pid_t pid; /* 线程id (gettid) */ + struct task_info* next; /* 指向下一个任务 */ +}; + +extern char* cgutil_subsys_table[]; + +#ifdef ENABLE_UT +#define static +#endif + +#define CHECK_GSCGROUP_TOP_DATABASE \ + ((strncmp(rel_path, GSCGROUP_TOP_DATABASE ":", sizeof(GSCGROUP_TOP_DATABASE)) == 0) && \ + (0 == strncmp(tmpstr, cgutil_passwd_user->pw_name, tmplen)) && \ + ('\0' == *(tmpstr + tmplen) || '/' == *(tmpstr + tmplen))) + +#define CHECK_GSCGROUP_CM \ + ((strncmp(rel_path, GSCGROUP_CM ":", sizeof(GSCGROUP_CM)) == 0) && \ + (0 == strncmp(cm_tmpstr, cgutil_passwd_user->pw_name, tmplen)) && \ + ('\0' == *(cm_tmpstr + tmplen) || '/' == *(cm_tmpstr + tmplen))) + +/* + ***************** STATIC FUNCTIONS ************************ + */ + + /* + * 函数名:cgpstree_free_task_list + * 描述:释放任务结构列表 + * 参数: + * head:任务列表的数据结构 + */ + +static void cgpstree_free_task_list(struct task_info* head) +{ + struct task_info* curr = head; + struct task_info* next = NULL; + + while (curr != NULL) { + next = curr->next; + + free(curr); + curr = NULL; + + if (next == NULL) + return; + + curr = next; + } +} +/** + * 函数名:cgptree_rec_free_group_tree + * 描述:递归释放组树结构 + * 参数: + * current:组树信息的数据结构 + * 功能:递归释放组树结构。首先判断当前节点是否为空,如果为空则直接返回。然后递归释放下一个组和子组。接着释放当前组的组名、相对路径、块IO读取比特速率、块IO读取操作数、块IO写入比特速率、块IO写入操作数、cpuset的cpu列表和任务列表的内存。最后释放当前组的内存,并将其置为NULL。 + */ + +static void cgptree_rec_free_group_tree(struct group_info* current) +{ + if (current == NULL) + return; + + // 递归释放下一个组和子组 + if (current->next != NULL) + cgptree_rec_free_group_tree(current->next); + if (current->child_head != NULL) + cgptree_rec_free_group_tree(current->child_head); + + // 释放组名、相对路径、块IO读取比特速率、块IO读取操作数、块IO写入比特速率、块IO写入操作数、 + // cpuset的cpu列表、任务列表 + if (current->grpname != NULL) + free(current->grpname); + if (current->relpath != NULL) + free(current->relpath); + if (current->blkio_bpsread != NULL) + free(current->blkio_bpsread); + if (current->blkio_iopsread != NULL) + free(current->blkio_iopsread); + if (current->blkio_bpswrite != NULL) + free(current->blkio_bpswrite); + if (current->blkio_iopswrite != NULL) + free(current->blkio_iopswrite); + if (current->cpuset_cpus != NULL) + free(current->cpuset_cpus); + if (current->task_head != NULL) + cgpstree_free_task_list(current->task_head); + + // 释放当前组的内存,并将其置为NULL + free(current); + current = NULL; + + return; +} + +/** + * 函数名:cgptree_free_group_tree + * 描述:释放一个控制器的组树结构 + * 参数: + * head:控制器的头组 + * 功能:释放一个控制器的组树结构。调用cgptree_rec_free_group_tree函数来递归地释放组树结构。 + */ +static void cgptree_free_group_tree(struct group_info* head) +{ + // 递归释放组树结构 + cgptree_rec_free_group_tree(head); + + return; +} + +/** + * 函数名:cgptree_get_cgroup + * 描述:根据相对路径获取cgroup结构 + * 参数: + * relpath:相对路径 + * 返回值:cgroup结构的指针 + * 功能:根据相对路径获取cgroup结构。首先分配一个新的cgroup结构,并根据相对路径初始化该结构。如果分配失败,则打印错误信息并返回NULL。然后从内核获取关于cgroup的所有信息。如果获取失败,则打印错误信息,并释放之前分配的cgroup结构,并返回NULL。如果获取成功,则返回cgroup结构的指针。 + */ +static struct cgroup* cgptree_get_cgroup(const char* relpath) +{ + int ret = 0; + struct cgroup* cg = NULL; + + /* 分配新的cgroup结构 */ + cg = cgroup_new_cgroup(relpath); + if (cg == NULL) { + ret = ECGFAIL; + fprintf(stdout, "failed to create the new cgroup for %s\n", cgroup_strerror(ret)); + return NULL; + } + + /* 从内核获取关于cgroup的所有信息 */ + ret = cgroup_get_cgroup(cg); + if (ret != 0) { + fprintf(stdout, "failed to get cgroup information for %s(%d)\n", cgroup_strerror(ret), ret); + cgroup_free(&cg); + return NULL; + } + + return cg; +} +代码解析: + +```c +/* + * function name: cgpstree_get_task_list + * description : 根据相对路径和挂载名获取任务列表 + * arguments : + * rel_path: 一个组的相对路径 + * ctl: 挂载信息的名称 + */ + static struct task_info* cgpstree_get_task_list(const char* rel_path, const char* ctrl) +{ + void* task_handle = NULL; + pid_t pid; + int error; + struct task_info* tinfo_head = NULL; + struct task_info* curr_tinfo = NULL; + struct task_info* prev_tinfo = NULL; + + // 调用cgroup_get_task_begin函数获取任务列表的起始位置,并返回错误码和任务句柄 + error = cgroup_get_task_begin(rel_path, ctrl, &task_handle, &pid); + + if (error && error != ECGEOF) + return NULL; + + // 循环遍历任务列表,直到遍历结束 + while (error != ECGEOF) { + // 分配内存用于保存任务信息 + curr_tinfo = (struct task_info*)calloc(1, sizeof(struct task_info)); + + if (curr_tinfo == NULL) { + // 如果内存分配失败,则释放已分配的内存并返回NULL + cgpstree_free_task_list(tinfo_head); + cgroup_get_task_end(&task_handle); + return NULL; + } + + // 将任务信息添加到链表中 + if (tinfo_head == NULL) + tinfo_head = curr_tinfo; + else + prev_tinfo->next = curr_tinfo; + + curr_tinfo->pid = pid; + + curr_tinfo->next = NULL; + + // 获取下一个任务的pid,并返回错误码 + error = cgroup_get_task_next(&task_handle, &pid); + + if (error && error != ECGEOF) { + cgpstree_free_task_list(tinfo_head); + cgroup_get_task_end(&task_handle); + return NULL; + } + + prev_tinfo = curr_tinfo; + } + + // 结束任务列表的遍历,并释放任务句柄 + cgroup_get_task_end(&task_handle); + return tinfo_head; +} + +/* error report */ +#define ERROR_REPORT(error, strings, grpname) \ + { \ + if (error) { \ + if (level <= *min_level || (*min_level == 0)) { \ + fprintf(stdout, \ + "NOTICE: Cgroup get %s failed for %s group: %s\n", \ + strings, \ + grpname, \ + cgroup_strerror(error)); \ + *min_level = level; \ + } \ + } \ + } + +/* + * function name: cgptree_get_group_info + * description : 获取指定挂载点和组信息curr_ginfo的组信息 + * arguments : + * curr_ginfo: 要更新的组信息 + * mount_info: 指定的挂载信息,如blkio, cpu, cpuset + * min_level: 传入传出参数,获取错误报告的最低级别 + */ +void cgptree_get_group_info(struct group_info* curr_ginfo, const struct cgroup_mount_point& mount_info, int* min_level) +{ + int error; + int level = 0; + + struct cgroup* cg = NULL; + struct cgroup_controller* cgc = NULL; + + char* rel_path = curr_ginfo->relpath; + + if (rel_path == NULL) + return; + + // 获取cg(cgroup结构体)对象,获取失败则返回 + cg = cgptree_get_cgroup(rel_path); + + /* error report has been done in cgptree_get_cgroup */ + if (cg == NULL) + return; + + level = curr_ginfo->depth; + + // 获取cgroup_controller(cgroup控制器结构体)对象,获取失败则返回 + cgc = cgroup_get_controller(cg, mount_info.name); + if (cgc == NULL) { + /* + * 只有最低级别的错误才需要报告。 + * 例如:如果Class不可用,则无需报告DefaultClass的错误消息。 + * 当出现Class错误时,树的扫描不会暂停,直到检查到"Backend"。 + */ + if (*min_level == 0 || (level <= *min_level)) { + fprintf(stderr, + "Notice: Cgroup add_controller for group \"%s\" of mount point \"%s\" failed\n", + curr_ginfo->grpname, + mount_info.name); + + *min_level = level; + } + + cgroup_free(&cg); + cgroup_free_controllers(cg); + return; + } + + // 获取cpu.shares的值 + if (0 == strcmp(mount_info.name, MOUNT_CPU_NAME)) { + error = cgroup_get_value_uint64(cgc, CPU_SHARES, &(curr_ginfo->cpu_shares)); + ERROR_REPORT(error, CPU_SHARES, curr_ginfo->grpname); + } + // 获取cpuset.cpus的值 + if (0 == strcmp(mount_info.name, MOUNT_CPUSET_NAME)) { + error = cgroup_get_value_string(cgc, CPUSET_CPUS, &(curr_ginfo->cpuset_cpus)); + ERROR_REPORT(error, CPUSET_CPUS, curr_ginfo->grpname); + } + // 获取cpuacct.cpus的值 + if (0 == strcmp(mount_info.name, MOUNT_CPUACCT_NAME)) { + error = cgroup_get_value_int64(cgc, CPUACCT_USAGE, &(curr_ginfo->cpuacct_usage)); + ERROR_REPORT(error, CPUACCT_USAGE, curr_ginfo->grpname); + } + + cgroup_free_controllers(cg); + cgroup_free(&cg); + return; +} +//上述两个函数的类似应用实例: +//- 在Linux系统中,可以使用该代码获取指定挂载点的组信息,并进行相应的处理。例如,可以利用获取到的cpu.shares值进行任务调度时的优先级设置,或者获取cpuset.cpus值进行CPU亲和性的设置。 +/* + * 函数名:cgptree_get_group_tree + * 功能:首次构建树形结构,扫描cgroup文件 + * 参数: + * mount_info: 指定的挂载信息,如blkio、cpu、cpuset + * 返回值: + * ginfo: 构建的group_info树的根节点 + */ +static struct group_info* cgptree_get_group_tree(const struct cgroup_mount_point& mount_info) +{ + int curr_depth = -1; // 当前深度 + int prev_depth = -1; // 上一级深度 + void* tree_handle = NULL; // 树的句柄 + int level = 0; // 层级 + int error; // 错误码 + size_t tmplen = 0; // 临时长度 + char* root_path = NULL; // 根路径 + char* rel_path = NULL, * tmpstr = NULL, * cm_tmpstr = NULL; // 相对路径、临时字符串、临时字符串 + int min_level = 0; // 最小层级 + + struct cgroup_file_info info; // cgroup文件信息 + struct group_info* curr_ginfo = NULL; // 当前组信息 + struct group_info* prev_ginfo = NULL; // 上一级组信息 + struct group_info* root_ginfo = NULL; // 根组信息 + + /* 开始遍历组树 */ + error = cgroup_walk_tree_begin(mount_info.name, "/", 0, &tree_handle, &info, &level); + if (error && error != ECGEOF) + return NULL; + + /* 保存挂载点的路径 */ + root_path = strdup(info.full_path); + if (root_path == NULL) { + cgroup_walk_tree_end(&tree_handle); + return NULL; + } + + error = cgroup_walk_tree_set_flags(&tree_handle, CGROUP_WALK_TYPE_PRE_DIR); + if (error) { + free(root_path); + root_path = NULL; + cgroup_walk_tree_end(&tree_handle); + return NULL; + } + + while (error != ECGEOF) { + /* 获取相对路径 */ + rel_path = (char*)(info.full_path + strlen(root_path)); + + tmpstr = rel_path + sizeof(GSCGROUP_TOP_DATABASE); + cm_tmpstr = rel_path + sizeof(GSCGROUP_CM); + tmplen = strlen(cgutil_passwd_user->pw_name); + + if ((CHECK_GSCGROUP_TOP_DATABASE || CHECK_GSCGROUP_CM) && info.type == CGROUP_FILE_TYPE_DIR) { + curr_ginfo = (struct group_info*)calloc(1, sizeof(struct group_info)); + + if (curr_ginfo == NULL) + goto error; + + curr_ginfo->depth = info.depth; + curr_ginfo->grpname = strdup(info.path); + + if (curr_ginfo->grpname == NULL) { + free(curr_ginfo); + curr_ginfo = NULL; + goto error; + } + + curr_ginfo->relpath = strdup(rel_path); + + if (curr_ginfo->relpath == NULL) { + free(curr_ginfo->grpname); + curr_ginfo->grpname = NULL; + free(curr_ginfo); + curr_ginfo = NULL; + goto error; + } + + /* 获取该组中的任务列表 */ + curr_ginfo->task_head = cgpstree_get_task_list(rel_path, mount_info.name); + + cgptree_get_group_info(curr_ginfo, mount_info, &min_level); + + curr_depth = info.depth; + + if (root_ginfo == NULL) { + root_ginfo = curr_ginfo; + } + else if (prev_depth == curr_depth) { + // 相同深度的组节点,将其视为兄弟节点 + prev_ginfo->next_sibling = curr_ginfo; + curr_ginfo->prev_sibling = prev_ginfo; + } + else if (prev_depth < curr_depth) { + // 深度增加,表示进入下一级组节点 + prev_ginfo->first_child = curr_ginfo; + curr_ginfo->parent = prev_ginfo; + } + else { // prev_depth > curr_depth + // 深度减少,表示返回上一级组节点 + int depth_diff = prev_depth - curr_depth; + struct group_info* parent = prev_ginfo->parent; + while (depth_diff > 0) { + parent = parent->parent; + depth_diff--; + } + parent->next_sibling = curr_ginfo; + curr_ginfo->prev_sibling = parent; + } + + prev_ginfo = curr_ginfo; + prev_depth = curr_depth; + } + + error = cgroup_walk_tree_next(&tree_handle, &info, &level); + } + + /* 结束遍历组树 */ + cgroup_walk_tree_end(&tree_handle); + + return root_ginfo; +} +/* + * 函数名称:cgptree_get_cgroup_new + * 功能描述:遍历挂载的控制器并获取树的group_info,填充相同group_info树中的不同子系统信息 + * 返回值: + * ginfo:完全构建的group_info树的根节点 + */ +static struct group_info* cgptree_get_cgroup_new() +{ + int error = 0; + void* ctrl_handle = NULL; + int count = 0; + + struct cgroup_mount_point info = { {0}, {0} }; + struct group_info* ginfo = NULL; + + // 获取第一个控制器 + error = cgroup_get_controller_begin(&ctrl_handle, &info); + if (error) { + fprintf(stderr, "获取控制器失败: %s\n", cgroup_strerror(error)); + return NULL; + } + + while (error != ECGEOF) { + // 判断控制器的名称,跳过不需要的控制器 + if (*info.name && strcmp(info.name, MOUNT_CPU_NAME) != 0 && strcmp(info.name, MOUNT_CPUSET_NAME) != 0 && + strcmp(info.name, MOUNT_CPUACCT_NAME) != 0) { + // 获取下一个控制器 + error = cgroup_get_controller_next(&ctrl_handle, &info); + if (error && error != ECGEOF) { + fprintf(stderr, "获取下一个控制器失败: %s\n", cgroup_strerror(error)); + break; + } + continue; + } + + // 对于新构建的树,从第一个扫描到的子系统挂载信息获取树节点信息 + if (!count) { + ginfo = cgptree_get_group_tree(info); + } + else { + // 后续的子系统组信息将填充到已构建的树中 + ginfo = cgptree_get_tree_info(info, ginfo); + } + + // 获取控制器的组树,如果组不可用,则跳过 + if (ginfo == NULL) { + fprintf(stderr, "注意:获取挂载点%s的树信息失败\n", info.name); + + error = cgroup_get_controller_next(&ctrl_handle, &info); + if (error && error != ECGEOF) { + fprintf(stderr, "获取下一个控制器失败: %s\n", cgroup_strerror(error)); + break; + } + continue; + } + + // 第一次分配内存以构建树 + // 下一次循环,不需要构建树的任何节点,只需填充树的节点信息 + count++; + + // 获取下一个控制器 + error = cgroup_get_controller_next(&ctrl_handle, &info); + if (error && error != ECGEOF) { + fprintf(stderr, "获取下一个控制器失败: %s\n", cgroup_strerror(error)); + break; + } + } + + // 结束控制器的获取 + (void)cgroup_get_controller_end(&ctrl_handle); + + return ginfo; +} + +/* + * 函数名称:cgptree_print_space + * 功能描述:根据级别打印空格 + * + */ +static void cgptree_print_space(int level) +{ + while (level-- > 0) + printf("\t"); +} + +/* + * 函数名称:cgptree_delete_group + * 功能描述:递归删除cgroup + */ +static void cgptree_delete_group(struct group_info* ginfo) +{ + if (NULL == ginfo) + return; + + if (ginfo->child_head != NULL) + cgptree_delete_group(ginfo->child_head); + + if (ginfo->next != NULL) + cgptree_delete_group(ginfo->next); + + cgexec_delete_cgroups(ginfo->relpath); +} +/* + * function name: cgptree_print_group + * description : 以树状样式打印组信息 + * arguments : + * ctlrname: 控制器名称 + * ginfo: 组信息的头部 + * level: 组的层级 + * + */ +#define BLKIO_STR_UPDATE(s) \ + { \ + char* q = NULL; \ + do { \ + q = strchr(s, '\n'); \ + if (q != NULL) \ + *q = ';'; \ + } while (q != NULL); \ + } + + /* + * @Description: 以树状方式打印cgroup信息 + * @IN ginfo: 组信息 + * @IN level: 组层级 + * @Return: void + * @See also: + */ + // 函数可以用于打印操作系统中的cgroup信息,以树状结构显示各个组的信息,包括组名、CPU份额、cpuset等。这在系统调优和性能分析中非常有用,可以帮助管理员更好地了解和管理系统资源限制。 + +static void cgptree_print_group_new(struct group_info* ginfo, int level) +{ + struct task_info* tinfo = ginfo->task_head; + int cnt = 0; + + cgptree_print_space(level); + + /* 打印组名 */ + fprintf(stdout, "- %s ", ginfo->grpname); + + /* 打印CPU份额 */ + fprintf(stdout, "(shares: %lu,", ginfo->cpu_shares); + + /* 打印cpuset */ + fprintf(stdout, " cpus: %s", ginfo->cpuset_cpus); + + if (cgutil_is_sles11_sp2 || cgexec_check_SLESSP2_version()) { + fprintf(stdout, ", weight: %lu", ginfo->blkio_weight); + + if ((ginfo->blkio_bpsread != NULL) && ginfo->blkio_bpsread[0]) { + BLKIO_STR_UPDATE(ginfo->blkio_bpsread); + fprintf(stdout, ", bpsread: \"%s\"", ginfo->blkio_bpsread); + } + if ((ginfo->blkio_iopsread != NULL) && ginfo->blkio_iopsread[0]) { + BLKIO_STR_UPDATE(ginfo->blkio_iopsread); + fprintf(stdout, ", iopsread: \"%s\"", ginfo->blkio_iopsread); + } + if ((ginfo->blkio_bpswrite != NULL) && ginfo->blkio_bpswrite[0]) { + BLKIO_STR_UPDATE(ginfo->blkio_bpswrite); + fprintf(stdout, ", bpswrite: \"%s\"", ginfo->blkio_bpswrite); + } + if ((ginfo->blkio_iopswrite != NULL) && ginfo->blkio_iopswrite[0]) { + BLKIO_STR_UPDATE(ginfo->blkio_iopswrite); + fprintf(stdout, ", iopswrite: \"%s\"", ginfo->blkio_iopswrite); + } + } + + fprintf(stdout, ")\n"); + + if (tinfo != NULL) + cgptree_print_space(level + 1); + + /* 打印组中的线程ID */ + while (tinfo != NULL) { + fprintf(stdout, "%8d ", (int)tinfo->pid); + + if (0 == (++cnt) % 5) { + fprintf(stdout, "\n"); + cgptree_print_space(level + 1); + } + + tinfo = tinfo->next; + } + + if (cnt) + fprintf(stdout, "\n"); + + fflush(stdout); + + /* 打印下一个组 */ + if (ginfo->child_head != NULL) + cgptree_print_group_new(ginfo->child_head, level + 1); + + if (ginfo->next != NULL) + cgptree_print_group_new(ginfo->next, level); +} + +/* + * function name: cgptree_free + * description : 释放控制器信息 + * arguments : + * cinfo_head: 控制器信息的头部 + * + */ + // 函数用于释放控制器信息,可以在代码执行完成后,释放相应的内存空间,避免内存泄漏。这对长时间运行的程序或者需要频繁创建和销毁控制器信息的程序非常重要。 + +static void cgptree_free(struct controller_info* cinfo_head) +{ + struct controller_info* curr = cinfo_head; + struct controller_info* next = NULL; + + if (NULL == curr) + return; + + /* 释放控制器数据结构 */ + while (curr != NULL) { + next = curr->next; + + if (curr->ctrl_name != NULL) + free(curr->ctrl_name); + if (curr->mount_point != NULL) + free(curr->mount_point); + + cgptree_free_group_tree(curr->group_head); + + free(curr); + curr = NULL; + curr = next; + } +} +// 该函数的功能是释放控制器信息以及相关资源。 +// 参数说明: +// - curr_cinfo: 当前控制器信息结构体指针 +// - curr_path: 当前路径字符串指针 +// - cinfo_head: 控制器信息链表头指针 +// - ctrl_handle: 控制器句柄指针 +void free_controller_list_resource(struct controller_info* curr_cinfo, + char* curr_path, + struct controller_info* cinfo_head, + void* ctrl_handle) +{ + if (curr_cinfo != NULL) { + if (curr_cinfo->ctrl_name != NULL) { + free(curr_cinfo->ctrl_name); // 释放当前控制器信息的控制器名称内存 + } + free(curr_cinfo); // 释放当前控制器信息的内存 + } + if (curr_path != NULL) { + free(curr_path); // 释放当前路径的内存 + } + cgptree_free(cinfo_head); // 释放控制器信息链表的内存 + cgroup_get_controller_end(&ctrl_handle); // 结束控制器操作 +} + +/* + * 函数名:cgptree_get_controller_list + * 描述:读取所有的cgroups并生成控制器信息 + * 返回值:控制器信息链表的头指针 + * + */ +static struct controller_info* cgptree_get_controller_list(void) +{ + int error = 0; + char* curr_path = NULL; // 当前路径字符串指针 + void* ctrl_handle = NULL; // 控制器句柄指针 + struct cgroup_mount_point info = { {0}, {0} }; // cgroups挂载点信息 + struct controller_info* cinfo_head = NULL; // 控制器信息链表头指针 + struct controller_info* curr_cinfo = NULL; // 当前控制器信息结构体指针 + struct controller_info* prev_cinfo = NULL; // 上一个控制器信息结构体指针 + struct group_info* ginfo = NULL; // 组信息结构体指针 + + error = cgroup_get_controller_begin(&ctrl_handle, &info); // 开始控制器操作 + if (error) { + fprintf(stderr, "get controller begin failed: %s\n", cgroup_strerror(error)); // 打印错误信息 + return NULL; + } + + while (error != ECGEOF) { + curr_path = strdup(info.path); // 复制当前路径字符串 + if (curr_path == NULL) { + free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); // 释放资源 + return NULL; + } + + /* + * 获取该控制器的组树 + * 如果组无效,则跳过 + */ + ginfo = cgptree_get_group_tree(info); // 获取组树 + if (NULL == ginfo) { + free(curr_path); // 释放当前路径的内存 + + error = cgroup_get_controller_next(&ctrl_handle, &info); // 获取下一个控制器 + if (error && error != ECGEOF) { + fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); // 打印错误信息 + break; + } + + continue; + } + + curr_cinfo = (struct controller_info*)calloc(1, sizeof(struct controller_info)); // 分配新的控制器信息结构体内存 + if (curr_cinfo == NULL) { + free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); // 释放资源 + return NULL; + } + + curr_cinfo->ctrl_name = strdup(info.name); // 复制控制器名称 + if (curr_cinfo->ctrl_name == NULL) { + free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); // 释放资源 + return NULL; + } + curr_cinfo->mount_point = strdup(info.path); // 复制挂载点路径 + if (curr_cinfo->mount_point == NULL) { + free_controller_list_resource(curr_cinfo, curr_path, cinfo_head, ctrl_handle); // 释放资源 + return NULL; + } + + curr_cinfo->group_head = ginfo; // 设置组信息 + + error = cgroup_get_controller_next(&ctrl_handle, &info); // 获取下一个控制器 + if (error && error != ECGEOF) { + fprintf(stderr, "get next controller failed: %s\n", cgroup_strerror(error)); // 打印错误信息 + if (curr_cinfo->ctrl_name != NULL) + free(curr_cinfo->ctrl_name); // 释放控制器信息的控制器名称内存 + if (curr_cinfo->mount_point != NULL) + free(curr_cinfo->mount_point); // 释放控制器信息的挂载点路径内存 + cgptree_free_group_tree(curr_cinfo->group_head); // 释放组信息内存 + free(curr_cinfo); // 释放控制器信息内存 + break; + } + + if (cinfo_head == NULL) + cinfo_head = curr_cinfo; + else + prev_cinfo->next = curr_cinfo; + + prev_cinfo = curr_cinfo; + if (curr_path != NULL) { + free(curr_path); // 释放当前路径的内存 + } + curr_cinfo = NULL; + } + + cgroup_get_controller_end(&ctrl_handle); // 结束控制器操作 + + return cinfo_head; +} +/* + **************** EXTERNAL FUNCTION ******************************** + */ + + /* + * function name: cgptree_display_cgroups + * description : 显示Cgroup树的信息 + * return value : + * -1: 异常 + * 0: 正常 + * 函数cgptree_display_cgroups用于显示Cgroup树的信息。它首先调用cgptree_get_cgroup_new()函数获取新的Cgroup树信息,并将结果保存在ginfo_new变量中。如果获取失败,则打印错误信息并返回 - 1。接下来,它遍历所有的挂载点,并打印相应的挂载信息。然后,它打印组树信息,调用cgptree_print_group_new()函数,并将ginfo_new作为参数传递。最后,它释放组树,调用cgptree_rec_free_group_tree()函数,并将ginfo_new作为参数传递。 + + */ + +int cgptree_display_cgroups(void) +{ + struct group_info* ginfo_new = NULL; + + // 获取新的Cgroup树信息 + ginfo_new = cgptree_get_cgroup_new(); + if (ginfo_new == NULL) { + /* 释放组信息 */ + fprintf(stderr, "无法获取新的Cgroup树信息!\n"); + return -1; + } + + /* 打印当前所有挂载点 */ + for (int i = 0; i < MOUNT_SUBSYS_KINDS; ++i) { + if (i == MOUNT_BLKIO_ID) + continue; + + if (i == 0) + fprintf(stdout, "挂载信息:\n"); + + fprintf(stdout, "%s:%s\n", cgutil_subsys_table[i], cgutil_opt.mpoints[i]); + } + + fprintf(stdout, "\n组树信息:\n"); + + /* 打印组信息 */ + cgptree_print_group_new(ginfo_new, 0); + + // 释放组树 + cgptree_rec_free_group_tree(ginfo_new); + + return 0; +} + +/* + * function name: cgptree_drop_cgroups + * description : 删除所有用户的cgroup + * return value : + * -1: 异常 + * 0: 正常 + * 函数cgptree_drop_cgroups用于删除所有用户的cgroup。它首先调用cgptree_get_controller_list()函数获取控制器列表信息,并将结果保存在cinfo_head变量中。如果获取失败,则打印错误信息并返回 - 1。接下来,它调用cgptree_delete_group()函数删除组树,将cinfo_head->group_head作为参数传递。最后,它释放控制器信息,调用cgptree_free()函数,并将cinfo_head作为参数传递。 + + */ +int cgptree_drop_cgroups(void) +{ + struct controller_info* cinfo_head = NULL; + + // 获取控制器列表 + cinfo_head = cgptree_get_controller_list(); + if (cinfo_head == NULL) { + fprintf(stderr, "无法获取Cgroup树信息!\n"); + return -1; + } + + // 删除组树 + cgptree_delete_group(cinfo_head->group_head); + + // 释放控制器信息 + cgptree_free(cinfo_head); + + return 0; +} + +/* + * function name: cgptree_drop_nodegroup_cgroups + * description : 删除所有用户的nodegroup cgroup + * return value : + * -1: 异常 + * 0: 正常 + * 函数cgptree_drop_nodegroup_cgroups用于删除所有用户的nodegroup cgroup。它首先调用cgptree_get_controller_list()函数获取控制器列表信息,并将结果保存在cinfo_head变量中。如果获取失败,则打印错误信息并返回 - 1。接下来,它遍历组树,查找与指定名称相匹配的组。如果找到了,则调用cgptree_delete_group()函数删除该组的子组,并调用cgexec_delete_cgroups()函数删除相应的cgroup。如果未找到与指定名称相匹配的组,但组树为空,则打印错误信息。如果未找到与指定名称相匹配的组,并且组树不为空,则打印错误信息。最后,它释放控制器信息,调用cgptree_free()函数,并将cinfo_head作为参数传递。 + */ +int cgptree_drop_nodegroup_cgroups(const char* name) +{ + struct controller_info* cinfo_head = NULL; + + // 获取控制器列表 + cinfo_head = cgptree_get_controller_list(); + if (cinfo_head == NULL) { + fprintf(stderr, "无法获取Cgroup树信息!\n"); + return -1; + } + + struct group_info* ginfo = cinfo_head->group_head->child_head; + while (ginfo != NULL) { + if (strcmp(ginfo->grpname, name) == 0) { + break; + } + + ginfo = ginfo->next; + } + + if (ginfo != NULL) { + if (ginfo->child_head != NULL) + cgptree_delete_group(ginfo->child_head); + + cgexec_delete_cgroups(ginfo->relpath); + } + else if (cinfo_head->group_head->child_head == NULL) { + fprintf(stderr, + "无法找到控制器名称为(%s)、挂载点为(%s)、组头为(%s)的cgroup (%s)。\n", + cinfo_head->ctrl_name, + cinfo_head->mount_point, + cinfo_head->group_head->grpname, name); + } + else { + fprintf(stderr, + "无法找到控制器名称为(%s)、挂载点为(%s)、组头为(%s)、子组头为(%s)的cgroup (%s)。\n", + cinfo_head->ctrl_name, + cinfo_head->mount_point, + cinfo_head->group_head->grpname, + cinfo_head->group_head->child_head->grpname, name); + } + + // 释放控制器信息 + cgptree_free(cinfo_head); + + return 0; +} -- 2.34.1 From 90dc7ce262e854d10a43755d36e9a094729460b2 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:03:09 +0800 Subject: [PATCH 09/56] Delete 'src/bin/gs_cgroup/main.cpp' --- src/bin/gs_cgroup/main.cpp | 1615 ------------------------------------ 1 file changed, 1615 deletions(-) delete mode 100644 src/bin/gs_cgroup/main.cpp diff --git a/src/bin/gs_cgroup/main.cpp b/src/bin/gs_cgroup/main.cpp deleted file mode 100644 index a8c0b933f..000000000 --- a/src/bin/gs_cgroup/main.cpp +++ /dev/null @@ -1,1615 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - *------------------------------------------------------------------------- - * - * main.cpp - * main function file for gs_cgroup utility - * - * IDENTIFICATION - * src/bin/gs_cgroup/main.cpp - * - * ------------------------------------------------------------------------- - */ -#include -#include -#include -#include -#include - -#include - -#include "securec.h" -#include "cgutil.h" -#include "pg_config.h" -#include "getopt_long.h" - -extern int CheckBackendEnv(const char* input_env_value); - -/* global variable to describe Cgroup configuration file */ -gscgroup_grp_t* cgutil_vaddr[GSCGROUP_ALLNUM] = {NULL}; - -/* global variable of gs_cgroup options */ -cgutil_opt_t cgutil_opt = {0}; - -/* the cpu count */ -int cgutil_cpucnt = 0; - -/* global variable to indicate the user of Cgroup configuration file */ -struct passwd* cgutil_passwd_user = NULL; - -/* global variable for version info */ -static char* cgutil_version = NULL; -/* all cores for OS */ -char cgutil_allset[CPUSET_LEN]; -/* memory set for OS */ -char cgutil_mems[CPUSET_LEN]; - -char* current_nodegroup = NULL; - -#define MAX_PATH_LEN 1024 /* the max length of the file path */ -#define MAX_BUF_SIZE 2048 /* the max size of the buffer */ -#define STATIC_CONFIG_FILE "cluster_static_config" /* the name of cluster static config file */ -#define PROG_NAME "gs_cgroup" -/* - * function name: usage - * description : gs_cgroup usage function - * - */ -static void usage(void) -{ - fprintf(stdout, - "\ngs_cgroup is used to manage the Gauss Cgroups on each node.\n" - "Usage:\n gs_cgroup [OPTION]...\n\n" - "OPTIONS:\n" - " -a [--abort] : the abort exception flag, should be used with '-E data'.\n" - " -b pct : backend group percentage\n" - " -B name : specify the group name together with '-u'\n" - " -c : create default control groups, \n" - " with '-S' and '-G' to create specified class groups and workload groups;\n" - " with '-N' to create control group of the specified logical cluster.\n" - " -d : drop all control groups, with '-S' and '-G' to drop specified groups\n" - " with '-N' to drop control group of the specified logical cluster.\n" - " -D mpoint : specify a mount point instead of default point: \"/dev/cgroup/subsystem\"\n" - " -E data : Exception data with the following format string: \n" - " blocktime=value (unit is second) \n" - " elapsedtime=value (unit is second) \n" - " allcputime=value (unit is second) \n" - " qualificationtime=value (unit is second) \n" - " cpuskewpercent=value (0 ~ 100) \n" - " spillsize=value (unit is MB) \n" - " broadcastsize=value (unit is MB) \n" - " these strings can be joined with ',' sperator.\n" - " -h [--help] : help information\n" - " -H : GAUSSHOME PATH for the specified user\n" - " -f : to specify cpu cores to use like this: a or a-b.\n" - " the argument is only used on Gaussdb:user group.\n" - " --fixed : allocate cpu cores by percentage for different groups.\n" - " -g pct : workload group percentage\n" - " -G name : specify the group name together with '-c', '-d', '-u' or '-E' \n" - " and '-S' option; ',' operator is used to join multiple groups.\n" - " -m : mount cgroups\n" - " -M : umount cgroups\n" - " -N [--group] name : specify the name of logical cluster.\n" - " -p : display the default cgroups configuration information.\n" - " with '-N' to display the control group configuration of the specified logical cluster.\n" - " -P : display all cgroups tree information of whole cluster.\n" - " --penalty : the penalty exception flag, should be used with '-E data'.\n" - " --recover : recover the group configure to last change by normal user.\n" - " with '-N' to recover control group of the specified logical cluster.\n" - " --refresh : refresh the cgroup group based on the configuration file.\n" - " with '-N' to refresh control group of the specified logical cluster.\n" - " --revert : revert the group to default.\n" - " -s pct : class group percentage\n" - " -S name : specify the Class name together with '-c', '-d', '-u' or '-E' option\n" - " if the class name is \"default\", it will be treated as \"DefaultClass\". \n" - " if this option is not set, class name will be 'DefaultClass' while with '-E'.\n" - " -t pct : top group percentage\n" - " -T name : specify the Top group name together with '-u' option\n" - " -u : modify the information of a specified class group with '-S', \n" - " or a specified top group with '-T', \n" - " or a specified workload group with '-G'.\n" - " with '-N' to update control group of the specified logical cluster.\n" - " -U name : the user name of database\n" - " -V [--version] : show the version.\n" - "\n" - "Examples:\n" - "Root user can execute:\n" - "gs_cgroup -U name -H path -c : create the default control groups.\n" - "gs_cgroup -U name -d : drop all control groups.\n" - "gs_cgroup -m: mount cgroup\n" - "gs_cgroup -M: umount cgroup\n" - "\n" - "Non-root user can execute:\n" - "gs_cgroup -p: display the control groups configuration information.\n" - "gs_cgroup -c -S class: create the default control groups for class\n" - "gs_cgroup -d -S class: drop all control groups of class\n" - "gs_cgroup -c -S class -G wg1: create wg1 groups for class.\n" - "gs_cgroup -d -S class -G wg2 : \n" - " drop wg2 groups of class, its child group is moved to its level.\n" - "gs_cgroup -u -T Gaussdb -t 70: \n" - " update CPU percentage of Gaussdb cgroup as 70%%.\n" - "gs_cgroup -u -f 2-8 -T Gaussdb: \n" - " update the CPU cores of Gaussdb:user cgroup as 2~8.\n" - "gs_cgroup -u --fixed -S class1 -s 40: \n" - " update the CPU cores percentage of class1 group as 40%% of the Top group: Class\n" - "gs_cgroup -S class -G wg -E \"blocktime=5,elapsedtime=5\" -a\n" - "gs_cgroup -S class -G wg -E \"spillsize=256,broadcastsize=100\" -a\n" - "gs_cgroup -c -N ngname: create control groups for the logical cluster ngname.\n" - "gs_cgroup -c -N ngname -S class -G wg1: create wg1 groups for class in the logical cluster ngname.\n" - "gs_cgroup -d -N ngname -S class: drop class control groups in the logical cluster ngname.\n" - "gs_cgroup -p -N ngname: display the control groups configuration information of the logical cluster ngname.\n" - "\n"); - - (void)fflush(stdout); -} -/* - * @Description: if more than one level of cgroup is specified, the reduntant groups are set to NULL - * @IN bkd: check if backend group is being updated - * @IN grp: check if workload group is being updated - * @IN cls: check if class group is being updated - * @IN top: check if top group is being updated - * @See also: - */ -void check_group_name_redundant(int bkd, int grp, int cls, int top) -{ - if (bkd) { - cgutil_opt.wdname[0] = '\0'; - cgutil_opt.clsname[0] = '\0'; - cgutil_opt.topname[0] = '\0'; - } - /* clsname must be left */ - else if (grp) { - cgutil_opt.bkdname[0] = '\0'; - cgutil_opt.topname[0] = '\0'; - } else if (cls) { - cgutil_opt.bkdname[0] = '\0'; - if (cgutil_opt.uflag) - cgutil_opt.wdname[0] = '\0'; - cgutil_opt.topname[0] = '\0'; - } else if (top) { - cgutil_opt.bkdname[0] = '\0'; - cgutil_opt.wdname[0] = '\0'; - cgutil_opt.clsname[0] = '\0'; - } -} -/* - * @Description: check percentage for different groups and cpusets. - * @IN bkd: check if backend group is being updated - * @IN grp: check if workload group is being updated - * @IN cls: check if class group is being updated - * @IN top: check if top group is being updated - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_percentage_value(int bkd, int grp, int cls, int top) -{ - /* fixed mode */ - if (cgutil_opt.fixed) { - /* - * it is not allowed if more than one group percentage is specified when updating - * cpuset by percentage - */ - if (bkd + cls + top + grp > 1) { - fprintf(stderr, "ERROR: redundant options of cpu core percentage. \n"); - return -1; - } else if (bkd + cls + top + grp == 0) { - return 0; - } - - check_group_name_redundant(bkd, grp, cls, top); - - /* check backend percentage, cpuset percentage range is 1-100 */ - if (cgutil_opt.uflag && bkd) { - if (cgutil_opt.bkdpct > 100 || cgutil_opt.bkdpct < 0) { - fprintf(stderr, - "ERROR: invalid value for cpu core percentage. " - "its range should be 0-100. \n"); - return -1; - } - cgutil_opt.setspct = cgutil_opt.bkdpct; - cgutil_opt.bkdpct = 0; - } - - /* check group percentage */ - if (cgutil_opt.uflag && grp) { - if (cgutil_opt.grppct > 100 || cgutil_opt.grppct < 0) { - fprintf(stderr, - "ERROR: invalid value for cpu core percentage. " - "its range should be 0-100. \n"); - return -1; - } - cgutil_opt.setspct = cgutil_opt.grppct; - cgutil_opt.grppct = 0; - } - - /* check class percentage */ - if (cgutil_opt.uflag && cls) { - if (cgutil_opt.clspct > 100 || cgutil_opt.clspct < 0) { - fprintf(stderr, - "ERROR: invalid value for cpu core percentage. " - "its range should be 0-100. \n"); - return -1; - } - cgutil_opt.setspct = cgutil_opt.clspct; - cgutil_opt.clspct = 0; - cgutil_opt.clssetpct = 1; - } - - /* check top group percentage */ - if (cgutil_opt.uflag && top) { - if (cgutil_opt.toppct > 100 || cgutil_opt.toppct < 0) { - fprintf(stderr, - "ERROR: invalid value for cpu core percentage. " - "its range should be 0-100. \n"); - return -1; - } - cgutil_opt.setspct = cgutil_opt.toppct; - cgutil_opt.toppct = 0; - } - - // if user set core percentage is 0, set a flag to show that user set - if (cgutil_opt.setspct == 0) - cgutil_opt.setfixed = 1; - } else { - if ((cgutil_opt.cflag || cgutil_opt.uflag) && bkd && (cgutil_opt.bkdpct >= 100 || cgutil_opt.bkdpct < 1)) { - fprintf(stderr, - "ERROR: invalid value for backend group dynamic percentage. " - "its range should be 1 ~ 99!\n"); - return -1; - } - - /* check backend percentage */ - if ((cgutil_opt.cflag || cgutil_opt.uflag) && grp && (cgutil_opt.grppct >= 100 || cgutil_opt.grppct < 1)) { - fprintf(stderr, - "ERROR: invalid value for workload group dynamic percentage. " - "its range should be 1 ~ 99!\n"); - return -1; - } - - /* check group percentage */ - if ((cgutil_opt.cflag || cgutil_opt.uflag) && cls && (cgutil_opt.clspct >= 100 || (cgutil_opt.clspct < 1))) { - fprintf(stderr, - "ERROR: invalid value for class group dynamic percentage. " - "its range should be 1 ~ 99!\n"); - return -1; - } - - /* check class percentage */ - if ((cgutil_opt.cflag || cgutil_opt.uflag) && top && (cgutil_opt.toppct >= 100 || cgutil_opt.toppct < 1)) { - fprintf(stderr, - "ERROR: invalid value for top group dynamic percentage. " - "its range should be 1 ~ 99!\n"); - return -1; - } - } - - return 0; -} - -/* - * @Description: check if the node group is valid. - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_node_group_name() -{ - char path[MAX_PATH_LEN]; - struct stat stat_buf; - - /* get the static configuration file */ - errno_t sret; - sret = memset_s(&stat_buf, sizeof(stat_buf), 0, sizeof(stat_buf)); - securec_check_errno(sret, , -1); - - char* exec_path = gs_getenv_r("GAUSSHOME"); - if (NULL == exec_path) { - fprintf(stderr, "ERROR: Get GAUSSHOME failed, please check.\n"); - return -1; - } - if (CheckBackendEnv(exec_path) != 0) { - return -1; - } - sret = snprintf_s(path, - MAX_PATH_LEN, - MAX_PATH_LEN - 1, - "%s/%s/%s_%s%s", - exec_path, - GSCGROUP_CONF_DIR, - GSCFG_PREFIX, - cgutil_passwd_user->pw_name, - GSCFG_SUFFIX); - securec_check_intval(sret, , -1); - - /* check if the file access */ - if (stat(path, &stat_buf) != 0) { - fprintf(stderr, "ERROR: the file %s doesn't exist.\n", path); - return -1; - } - - return 0; -} - -/* - * @Description: check input for security - * @IN input: input string - * @Return: void - * @See also: - */ -static void check_input_for_security(char* input) -{ - char* danger_token[] = {"|", ";", "&", "$", "<", ">", "`", "\\", "!", "\n", NULL}; - - for (int i = 0; danger_token[i] != NULL; ++i) { - if (strstr(input, danger_token[i]) != NULL) { - printf("invalid token \"%s\"\n", danger_token[i]); - exit(1); - } - } -} - -/* - * @Description: Check whether the name of class, - * Class group and Workload group is valid. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_name_valid(void) -{ - int namelen = GPNAME_LEN / 2 - 1; /* max name length */ - errno_t sret; - - /* check class name and class exception data */ - if (*cgutil_opt.clsname == '\0' && *cgutil_opt.edata) { - fprintf(stdout, - "NOTICE: if not specify class name but exceptional data is valid, " - "class name will be \"%s\"!\n", - GSCGROUP_DEFAULT_CLASS); - sret = snprintf_s(cgutil_opt.clsname, GPNAME_LEN, GPNAME_LEN - 1, "%s", GSCGROUP_DEFAULT_CLASS); - securec_check_intval(sret, , -1); - } - - /* check class name length */ - if (strlen(cgutil_opt.clsname) > (size_t)namelen) { - *cgutil_opt.clsname = '\0'; - fprintf(stderr, - "ERROR: The name of Class group is beyond " - "its dedicated size which is %d bytes.\n", - namelen); - - return -1; - } - - /* check workload group name length */ - if (strlen(cgutil_opt.wdname) > (size_t)namelen - 3) { - *cgutil_opt.wdname = '\0'; - fprintf(stderr, - "ERROR: The name of Workload group is beyond " - "its dedicated size which is %d bytes.\n", - namelen - 3); - - return -1; - } - - return 0; -} - -/* - * @Description: check input values. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_input_valid(void) -{ - /* check group name with flag '--fixed' */ - if (*cgutil_opt.clsname == '\0' && *cgutil_opt.wdname == '\0' && *cgutil_opt.bkdname == '\0' && - *cgutil_opt.topname == '\0' && cgutil_opt.fixed) { - fprintf(stderr, "ERROR: Please specify a group name with flag \"--fixed\"\n"); - return -1; - } - - /* check flag '--fixed' and '-u' */ - if (cgutil_opt.fixed && 0 == cgutil_opt.uflag) { - fprintf(stderr, "ERROR: Please specify \'--fixed\' flag together with \'-u\' flag.\n"); - return -1; - } - - /* check group name with flag '-f' */ - if ((*cgutil_opt.clsname || *cgutil_opt.wdname || *cgutil_opt.bkdname || - (*cgutil_opt.topname && - (0 != strncmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE, sizeof(GSCGROUP_TOP_DATABASE))))) && - *cgutil_opt.sets) { - fprintf(stderr, "ERROR: Only specify \'-f\' option on Gaussdb Group.\n"); - return -1; - } - - /* users cannot use -f and --fixed at the same time */ - if (cgutil_opt.fixed && *cgutil_opt.sets) { - fprintf(stderr, "ERROR: Please specify one option from \'-f\',\'--fixed\'.\n"); - return -1; - } - - /* get current mount points */ - if (cgexec_get_mount_points() < 0) { - return -1; - } - - /* check '-c', '-d', '-u' flag */ - if ((cgutil_opt.cflag && cgutil_opt.dflag) || (cgutil_opt.cflag && cgutil_opt.uflag) || - (cgutil_opt.uflag && cgutil_opt.dflag)) { - fprintf(stderr, "ERROR: please only specify one option from '-c', '-d' and '-u'.\n"); - return -1; - } - - /* check '-e' flag */ - if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_ERROR)) { - fprintf(stderr, "ERROR: abort and penalty cannot be specified together!\n"); - return -1; - } - - /* check exception data from '-e' flag */ - if (cgutil_opt.clsname[0] == '\0' && IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_PENALTY)) { - fprintf(stderr, "ERROR: you must specify a class name with penalty!\n"); - return -1; - } - - /* set default exception data without '--penalty', '--abort' and '-a' flag */ - if (*cgutil_opt.edata && IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) { - cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_PENALTY); - fprintf(stdout, "NOTICE: if do not specify exceptional action, default is penalty!\n"); - } - - /* check '--refresh', '--revert' and '--recover' flag */ - if ((cgutil_opt.cflag || cgutil_opt.dflag || cgutil_opt.uflag) && - (cgutil_opt.refresh || cgutil_opt.revert || cgutil_opt.recover)) { - fprintf(stderr, - "ERROR: you cannot specify option '-c', '-u' or '-d' with " - "'--refresh' or '--revert' or '--recover'!\n"); - return -1; - } - - /* check '--recover' flag */ - if ((geteuid() == 0) && cgutil_opt.recover) { - fprintf(stderr, "ERROR: you cannpt specify option '--recover' by root user!\n"); - return -1; - } - - return 0; -} - -/* - * @Description: check user info with flags. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_user_process(void) -{ - /* check root user process */ - if ((geteuid() == 0) && ((cgutil_opt.cflag || cgutil_opt.display || cgutil_opt.uflag || cgutil_opt.dflag) && - cgutil_opt.user[0] == '\0')) { - fprintf(stderr, - "ERROR: you must specify the user name with '-c', '-d', '-p' or '-u' " - "while running as root user.\n"); - return -1; - } - - /* check non-root user process */ - if (geteuid() && cgutil_opt.user[0] != '\0') { - fprintf(stderr, "ERROR: you can't specify the user name while running as non-root user.\n"); - return -1; - } - - /* check user info for '-P' flag */ - if (0 == geteuid() && cgutil_opt.ptree && '\0' == *cgutil_opt.user) { - fprintf(stderr, - "ERROR: you must specify the user name when running as root user " - "to display the cgroup tree.\n"); - return -1; - } - - /* check non-root user info for '-M' flag */ - if ((cgutil_opt.mflag || cgutil_opt.umflag) && geteuid()) { - fprintf(stderr, "ERROR: you must run mount or umount cgroup by root user!\n"); - return -1; - } - - return 0; -} - -/* - * @Description: check all flags. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_flag_process(void) -{ - /* create flag process */ - if (cgutil_opt.cflag) { - /* check top and backend group name */ - if (cgutil_opt.topname[0] != '\0' || cgutil_opt.bkdname[0] != '\0') { - fprintf(stderr, - "ERROR: you can't specify the Top group or backend group" - " during creating cgroup!\n"); - return -1; - } - - /* check workload group name and class name */ - if (cgutil_opt.wdname[0] != '\0' && cgutil_opt.clsname[0] == '\0') { - fprintf(stderr, - "ERROR: You can' specify the group name without" - " specifying the class name during creating cgroup!\n"); - return -1; - } - - if (*cgutil_opt.wdname && NULL != strchr(cgutil_opt.wdname, ':')) { - fprintf(stderr, "ERROR, workload group cannot be named with ':'. \n"); - return -1; - } - } - - /* delete flag process */ - if (cgutil_opt.dflag) { - /* check top and backend group name */ - if (cgutil_opt.topname[0] != '\0' || cgutil_opt.bkdname[0] != '\0') { - fprintf(stderr, - "ERROR: you can't specify the Top group or backend group" - " during dropping cgroup!\n"); - return -1; - } - } - - /* update flag process */ - if (cgutil_opt.uflag && - ('\0' == cgutil_opt.topname[0] && '\0' == cgutil_opt.bkdname[0] && '\0' == cgutil_opt.clsname[0])) { - fprintf(stderr, "ERROR: please specify the Group name when updating!\n"); - return -1; - } - return 0; -} - -/* - * @Description: check group names. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_group_name_process(int top, int bkd) -{ - /* check class name and percentage */ - if (cgutil_opt.clspct && '\0' == cgutil_opt.clsname[0]) { - fprintf(stderr, - "ERROR: please specify the Class name " - "together with Class percent!\n"); - return -1; - } - - /* check workload group name and percentage */ - if (cgutil_opt.grppct && '\0' == cgutil_opt.wdname[0]) { - fprintf(stderr, - "ERROR: please specify the Workload name " - "together with Workload percent!\n"); - return -1; - } - - /* workload group name special process */ - if (cgutil_opt.wdname[0] != '\0') { - if ((NULL == strchr(cgutil_opt.wdname, ':') && 0 == strcmp(cgutil_opt.wdname, GSCGROUP_TOP_WORKLOAD)) || - (NULL != strchr(cgutil_opt.wdname, ':') && - 0 == strncmp(cgutil_opt.wdname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1))) { - fprintf(stderr, "ERROR: can't do any operation on %s group!\n", GSCGROUP_TOP_WORKLOAD); - return -1; - } - } - - /* check timeshare group name */ - if (cgutil_opt.wdname[0] && (0 == strcmp(cgutil_opt.wdname, GSCGROUP_RUSH_TIMESHARE) || - 0 == strcmp(cgutil_opt.wdname, GSCGROUP_HIGH_TIMESHARE) || - 0 == strcmp(cgutil_opt.wdname, GSCGROUP_MEDIUM_TIMESHARE) || - 0 == strcmp(cgutil_opt.wdname, GSCGROUP_LOW_TIMESHARE))) { - fprintf(stderr, - "ERROR: can't specify the name of Workload group the same as " - "the name of default Timeshare Group!\n"); - return -1; - } - - /* top group name special process */ - if (cgutil_opt.topname[0] != '\0') { - if (!cgutil_opt.uflag) { - fprintf(stderr, "ERROR: please specify the option '-u' when using top name!\n"); - return -1; - } - - /* check top percentage with '-f' flag */ - if (!cgutil_opt.fixed && !*cgutil_opt.sets && !cgutil_opt.toppct) { - fprintf(stderr, "ERROR: please specify the top dynamic percent when using top name!\n"); - return -1; - } else if (cgutil_opt.fixed && - !(cgutil_opt.setspct || cgutil_opt.toppct || top)) { - fprintf(stderr, - "ERROR: please specify the cpu core percent or IO values " - "when updating fixed values!\n"); - return -1; - } - } - - /* backend group name special process */ - if (cgutil_opt.bkdname[0] != '\0') { - if (!cgutil_opt.uflag) { - fprintf(stderr, - "ERROR: please specify the option '-u' " - "when using backend name!\n"); - return -1; - } - - /* check backend percent with '--fixed' flag */ - if (cgutil_opt.fixed && !(cgutil_opt.setspct || cgutil_opt.bkdpct || bkd)) { - fprintf(stderr, - "ERROR: please specified the cpu core percent or IO values " - "when updating fixed values!\n"); - return -1; - } - } - - /* check backend name and percentage */ - if (cgutil_opt.bkdpct && '\0' == cgutil_opt.bkdname[0]) { - fprintf(stderr, - "ERROR: please specify the backend name " - "together with backend percent!\n"); - return -1; - } - /* check backend name and percentage */ - if (cgutil_opt.toppct && '\0' == cgutil_opt.topname[0]) { - fprintf(stderr, - "ERROR: please specify the top cgroup name " - "together with top percent!\n"); - return -1; - } - - /* Check if the node group has been created or will be created */ - if ('\0' != cgutil_opt.nodegroup[0]) { - if ((cgutil_opt.clsname[0] != '\0' || cgutil_opt.wdname[0] != '\0' || cgutil_opt.refresh) && - -1 == check_node_group_name()) { - fprintf(stderr, "ERROR: please check if the node group exists!\n"); - return -1; - } - - /* can't run command by root user */ - if (geteuid() == 0) { - fprintf(stderr, - "ERROR: please execute command by non-root user " - "when the node group is specified!\n"); - return -1; - } - - if (0 == strcmp(cgutil_opt.nodegroup, GSCGROUP_TOP_CLASS) || - 0 == strcmp(cgutil_opt.nodegroup, GSCGROUP_TOP_BACKEND)) { - fprintf(stderr, "ERROR: the name of logical cluster can't be 'Class' or 'Backend'.\n"); - return -1; - } - - if (!cgutil_opt.cflag && !cgutil_opt.dflag && !cgutil_opt.uflag && !cgutil_opt.display && !cgutil_opt.recover && - !cgutil_opt.refresh && ('\0' == *cgutil_opt.edata)) { - fprintf(stderr, "ERROR: please specify logical cluster with -c/-d/-u/--recover/--refresh option!\n"); - return -1; - } - } - - /* Check if the rename flag is set together with nodegroup */ - if (cgutil_opt.rename && '\0' == cgutil_opt.nodegroup[0]) { - fprintf(stderr, "ERROR: please specify the rename flag together with nodegroup name!\n"); - return -1; - } - - /* set the name when there is no rename flag */ - if (!cgutil_opt.rename && '\0' != cgutil_opt.nodegroup[0]) - current_nodegroup = cgutil_opt.nodegroup; - - return 0; -} - -/* - * @Description: check user name. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_user_name(void) -{ - /* user name is valid */ - if (cgutil_opt.user[0]) { - /* check it's root user */ - if (0 == strcmp(cgutil_opt.user, "root")) { - fprintf(stderr, "ERROR: can't specify the user name as root.\n"); - return -1; - } else /* get the user id and group id */ - { - cgutil_passwd_user = getpwnam(cgutil_opt.user); - if (NULL == cgutil_passwd_user) { - fprintf(stderr, - "ERROR: can't get the uid and gid of %s.\n" - "HINT: please check the specified user name.\n", - cgutil_opt.user); - return -1; - } - } - } else { - /* save current user info */ - cgutil_passwd_user = getpwuid(geteuid()); - if (NULL == cgutil_passwd_user) { - fprintf(stderr, - "ERROR: can't get the cgutil_passwd_user.\n" - "HINT: please check the running user!\n"); - return -1; - } - } - - return 0; -} - -/* - * @Description: check all input whether is valid. - * @IN bkd: backend group id - * @IN grp: group id - * @IN cls: class id - * @IN top: top group id - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_input_isvalid(int bkd, int grp, int cls, int top) -{ - /* check list */ - if (check_name_valid() == -1 || check_input_valid() == -1 || check_user_name() == -1 || - check_percentage_value(bkd, grp, cls, top) == -1 || check_group_name_process(top, bkd) == -1 || - check_user_process() == -1 || check_flag_process() == -1) { - return -1; - } - - return 0; -} - -/* - * @Description: check cpuset value is valid. - * @IN cpuset: cpuset value - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_cpuset_value_valid(char* cpuset) -{ - char* bad = NULL; - int a = -1; - int b = -1; - char* p = NULL; - - if (*cpuset == '-') { - fprintf(stderr, "ERROR: please specify the cpuset with a valid value.\n"); - return -1; - } - - p = strchr(cpuset, '-'); - - /* check "cpuset" value is like this: a-b */ - if (p == NULL) { - a = (int)strtol(cpuset, &bad, 10); - if ((bad != NULL) && *bad) { - fprintf(stderr, "ERROR: please specify the cpuset with \"a-b\" or \"a\"!\n"); - return -1; - } - - b = a; - } else { - *p++ = '\0'; - - a = (int)strtol(cpuset, &bad, 10); - if ((bad != NULL) && *bad) { - fprintf(stderr, "ERROR: please specify the cpuset with a valid value.\n"); - return -1; - } - - b = (int)strtol(p, &bad, 10); - if ((bad != NULL) && *bad) { - fprintf(stderr, "ERROR: please specify the cpuset with a valid value.\n"); - return -1; - } - } - - if ((a < 0) || (b < 0) || (a > b) || (b >= cgutil_cpucnt)) { - fprintf(stderr, "ERROR: please specify the cpuset with a valid value.\n"); - return -1; - } - - int rcs = sprintf_s(cgutil_opt.sets, CPUSET_LEN, "%d-%d", a, b); - securec_check_intval(rcs, , -1); - - return 0; -} - -/* - * @Description: check config flag. - * @IN void - * @Return: 1: OK 0: Not OK - * @See also: - */ -static int check_config_flag(void) -{ - if (cgutil_opt.cflag || (cgutil_opt.dflag && (*cgutil_opt.clsname || *cgutil_opt.nodegroup)) || cgutil_opt.uflag || - cgutil_opt.display || (*cgutil_opt.edata && *cgutil_opt.clsname) || cgutil_opt.refresh || cgutil_opt.upgrade || - cgutil_opt.revert || cgutil_opt.recover) - return 1; - - return 0; -} - -/* - * @Description: initialize cgroup config. - * @IN void - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int initialize_cgroup_config(void) -{ - char* hpath = NULL; - errno_t sret; - - /* retrieve the information of configure file; if it doesn't, create one */ - if (check_config_flag() > 0) { - if (geteuid() == 0) { - if ('\0' == cgutil_opt.hpath[0]) { - fprintf(stderr, - "ERROR: you need specify the GAUSSHOME path " - "when runing as root user!\n"); - return -1; - } - } else { - if (NULL == (hpath = gs_getenv_r("GAUSSHOME"))) { - fprintf(stderr, "ERROR: environment variable $GAUSSHOME is not set!\n"); - return -1; - } - if (CheckBackendEnv(hpath) != 0) { - return -1; - } - sret = snprintf_s(cgutil_opt.hpath, sizeof(cgutil_opt.hpath), sizeof(cgutil_opt.hpath) - 1, "%s", hpath); - securec_check_intval(sret, , -1); - } - - if (-1 == cgconf_parse_config_file()) { - if (cgutil_opt.dflag && '\0' != cgutil_opt.nodegroup[0]) { - fprintf(stderr, "WARNING: failed to parse the node group configure file!\n"); - } else { - fprintf(stderr, "FATAL: failed to parse the configure file!\n"); - return -1; - } - } - } - - return 0; -} - -/* - * @Description: check and get group percent. - * @IN percent: input percent - * @IN gtype: group type: class, workload or top - * @Return: -1: abnormal 0: normal - * @See also: - */ -static int check_and_get_group_percent(char* percent, char* gtype) -{ - char* bad = NULL; - - if (strcmp(gtype, "top") == 0) { - cgutil_opt.toppct = (int)strtol(percent, &bad, 10); - } - else if (strcmp(gtype, "class") == 0) { - cgutil_opt.clspct = (int)strtol(percent, &bad, 10); - } - else if (strcmp(gtype, "workload") == 0) { - cgutil_opt.grppct = (int)strtol(percent, &bad, 10); - } - else if (strcmp(gtype, "backend") == 0) { - cgutil_opt.bkdpct = (int)strtol(percent, &bad, 10); - } - else { - return -1; - } - - if ((bad != NULL) && *bad) { - fprintf(stderr, "ERROR: incorrect %s percent %s!\n", gtype, percent); - return -1; - } - - return 0; -} - -/* - * function name: parse_options - * description : parse the option of gs_cgroup utility - * arguments : as main function arguments - * return value : - * -1: abnormal - * 0: normal - */ -static struct option long_options[] = {{"help", no_argument, NULL, 'h'}, - {"version", no_argument, NULL, 'V'}, - {"abort", no_argument, NULL, 'a'}, - {"group", required_argument, NULL, 'N'}, - {"penalty", no_argument, NULL, 1}, - {"upgrade", no_argument, NULL, 2}, - {"refresh", no_argument, NULL, 3}, - {"revert", no_argument, NULL, 4}, - {"fixed", no_argument, NULL, 5}, - {"recover", no_argument, NULL, 6}, - {"rename", no_argument, NULL, 7}, - {NULL, 0, NULL, 0}}; - -static int parse_options(int argc, char** argv) -{ - int c; - int option_index; - int bkd = 0; - int grp = 0; - int cls = 0; - int top = 0; - int last = 0; - errno_t sret; - - sret = memset_s(&cgutil_opt, sizeof(cgutil_opt_t), 0, sizeof(cgutil_opt_t)); - securec_check_errno(sret, , -1); - - /* option parse */ - while ((c = getopt_long( - argc, argv, "ab:B:cdD:E:f:hH:g:G:mMN:pPr:R:s:S:t:T:uU:Vw:W:", long_options, &option_index)) != -1) { - switch (c) { - case 'a': /* abort */ - if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) - cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ABORT); - else - cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ERROR); - break; - case 'b': /* backend group percentage */ - if (check_and_get_group_percent(optarg, "backend") == -1) - return -1; - - bkd = 1; - break; - case 'B': /* backend group name */ - sret = strncpy_s(cgutil_opt.bkdname, GPNAME_LEN, optarg, GPNAME_LEN - 1); - securec_check_errno(sret, , -1); - - check_input_for_security(cgutil_opt.bkdname); - - break; - case 'c': /* create group */ - cgutil_opt.cflag = 1; - break; - case 'd': /* drop group */ - cgutil_opt.dflag = 1; - break; - case 'D': /* mount point */ - cgutil_opt.mpflag = 1; - sret = strncpy_s(cgutil_opt.mpoint, MAXPGPATH, optarg, MAXPGPATH - 1); - securec_check_errno(sret, , -1); - - check_input_for_security(cgutil_opt.mpoint); - - last = strlen(cgutil_opt.mpoint) - 1; - if ('/' == cgutil_opt.mpoint[last]) - cgutil_opt.mpoint[last] = '\0'; - break; - case 'E': /* Exceptional data */ - sret = strncpy_s(cgutil_opt.edata, EXCEPT_LEN, optarg, EXCEPT_LEN - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.edata); - break; - case 'h': /* help */ - usage(); - exit(0); - case 'H': /* GAUSSHOME path */ - sret = strncpy_s(cgutil_opt.hpath, MAXPGPATH, optarg, MAXPGPATH - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.hpath); - break; - case 'f': /* core numbers */ - if (check_cpuset_value_valid(optarg) == -1) - return -1; - break; - case 'g': /* workload group percentage */ - if (check_and_get_group_percent(optarg, "workload") == -1) - return -1; - - grp = 1; - break; - case 'G': /* workload group name for "gpname:gplevel" */ - - sret = strncpy_s(cgutil_opt.wdname, GPNAME_LEN, optarg, GPNAME_LEN - 1 - 2); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.wdname); - break; - case 'm': /* mount cgroup */ - cgutil_opt.mflag = 1; - break; - case 'M': /* umount cgroup */ - cgutil_opt.umflag = 1; - break; - case 'N': /* Nodegroup information */ - sret = strncpy_s(cgutil_opt.nodegroup, GPNAME_LEN, optarg, GPNAME_LEN - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.nodegroup); - break; - case 'p': /* display gscgroup.cfg information */ - cgutil_opt.display = 1; - break; - case 'P': /* display Cgroup tree information */ - cgutil_opt.ptree = 1; - break; - case 's': /* Class group percentage */ - if (check_and_get_group_percent(optarg, "class") == -1) - return -1; - - cls = 1; - break; - case 'S': /* Class group name */ - if (strchr(optarg, ':') != NULL) { - fprintf(stderr, "ERROR, class cannot be named with ':'. \n"); - return -1; - } - sret = strncpy_s(cgutil_opt.clsname, GPNAME_LEN, optarg, GPNAME_LEN - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.clsname); - break; - case 't': /* Top group percentage */ - if (check_and_get_group_percent(optarg, "top") == -1) - return -1; - - top = 1; - break; - case 'T': /* Top group name */ - sret = strncpy_s(cgutil_opt.topname, GPNAME_LEN, optarg, GPNAME_LEN - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.topname); - break; - case 'u': /* update flag */ - cgutil_opt.uflag = 1; - break; - case 'U': /* user name */ - sret = strncpy_s(cgutil_opt.user, USERNAME_LEN, optarg, USERNAME_LEN - 1); - securec_check_errno(sret, , -1); - check_input_for_security(cgutil_opt.user); - break; - case 'V': /* version */ - cgutil_version = DEF_GS_VERSION; - return 0; - case 1: - if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) - cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_PENALTY); - else - cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ERROR); - break; - case 2: - cgutil_opt.upgrade = 1; - break; - case 3: - cgutil_opt.refresh = 1; - break; - case 4: - cgutil_opt.revert = 1; - break; - case 5: - cgutil_opt.fixed = 1; - break; - case 6: - cgutil_opt.recover = 1; - break; - case 7: - cgutil_opt.rename = 1; - break; - default: - fprintf(stderr, "ERROR: incorrect option: %s\n.", optarg); - usage(); - return -1; - } - } - - return check_input_isvalid(bkd, grp, cls, top); -} - -/* - * function name: main - * description : main entry of gs_cgroup utility - * arguments : main function default arguments - */ -int main(int argc, char** argv) -{ - char* cpuset = NULL; - int ret = 0; - - if (argc < 2) { - usage(); - exit(-1); - } - - // log output redirect - init_log(PROG_NAME); - - /* print the log about arguments of gs_cgroup */ - char arguments[MAX_BUF_SIZE] = {0x00}; - for (int i = 0; i < argc; i++) { - errno_t rc = strcat_s(arguments, MAX_BUF_SIZE, argv[i]); - size_t len = strlen(arguments); - if (rc != EOK || len >= (MAX_BUF_SIZE - 2)) - break; - arguments[len] = ' '; - arguments[len + 1] = '\0'; - } - write_log("The gs_cgroup run with the following arguments: [%s].\n", arguments); - - /* get the cpu count value */ - cgutil_cpucnt = gsutil_get_cpu_count(); - - if (cgutil_cpucnt == -1) { - fprintf(stderr, - "get cpu core range failed, please check if \"/proc/cpuinfo\"" - " or \"/sys/devices/system\" is acceptable. \n"); - exit(-1); - } - - int rc = sprintf_s(cgutil_allset, sizeof(cgutil_allset), "%d-%d", 0, cgutil_cpucnt - 1); - securec_check_intval(rc, , -1); - - /* parse the options */ - ret = parse_options(argc, argv); - if (-1 == ret) { - fprintf(stderr, "HINT: please run 'gs_cgroup -h' to display the usage!\n"); - exit(-1); - } - - if (cgutil_version != NULL) { - fprintf(stdout, "gs_cgroup %s\n", cgutil_version); - return 0; - } - - if (geteuid() == 0 && cgutil_opt.mflag) { - cgexec_mount_cgroups(); - } - - if (geteuid() == 0 && cgutil_opt.umflag && !cgutil_opt.dflag) { - cgexec_umount_cgroups(); - exit(0); - } - - /* retrieve the information of configure file; if it doesn't, create one */ - if (initialize_cgroup_config() == -1) - return -1; - - /* check upgrade flag */ - if (cgutil_opt.upgrade) { - cgutil_opt.refresh = 1; - - /* maybe we need not do upgrade */ - if (cgexec_check_mount_for_upgrade() == -1) { - goto error; - } - } - - if (cgutil_opt.cflag) { - /* run as root user */ - if (geteuid() == 0 && cgutil_opt.upgrade == 0) { - /* check if cgroups have been mounted if it doesn't specify mflag */ - if (-1 == (ret = cgexec_mount_root_cgroup())) { - goto error; - } - } - } - - /* initialize libcgroup */ - ret = cgroup_init(); - if (ret) { - fprintf(stderr, - "FATAL: libcgroup initialization failed: %s\n" - "please run 'gs_cgroup -m' to " - "mount cgroup by root user!\n", - cgroup_strerror(ret)); - goto error; - } - - /* get memory set */ - if (-1 == cgexec_get_cgroup_cpuset_info(TOPCG_ROOT, &cpuset)) { - fprintf(stderr, "ERROR: failed to get cpusets and mems during initialization.\n"); - goto error; - } - - rc = snprintf_s(cgutil_allset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); - securec_check_intval(rc, , -1); - free(cpuset); - cpuset = NULL; - - /* create/delete/update operation */ - if (cgutil_opt.cflag) { - if (cgexec_create_groups() == -1) { - goto error; - } - } else if (cgutil_opt.dflag) { - if (cgexec_drop_groups() == -1) { - goto error; - } - } else if (cgutil_opt.uflag) { - if (cgexec_update_groups() == -1) { - goto error; - } - } else if (cgutil_opt.revert) { - if (cgexec_revert_groups() == -1) { - goto error; - } - } - - /* refresh current groups */ - if (cgutil_opt.refresh) { - if (cgexec_refresh_groups() == -1) { - goto error; - } - } - - /* recover the last changes of groups */ - if (cgutil_opt.recover) { - if (cgexec_recover_groups() == -1) { - goto error; - } - } - - /* process the exceptional data */ - if (*cgutil_opt.edata && *cgutil_opt.clsname && -1 == cgexcp_class_exception()) - goto error; - - /* display the cgroup configuration file information */ - if (cgutil_opt.display) - cgconf_display_groups(); - - /* display the cgroup tree information */ - if (cgutil_opt.ptree) { - if (cgptree_display_cgroups() == -1) { - goto error; - } - } - - if (cgutil_vaddr[0] != NULL) - (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - - return 0; -error: - if (cgutil_vaddr[0] != NULL) - (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); - write_log("gs_cgroup execution error.\n"); - exit(-1); -} - -#ifdef ENABLE_UT -void cgroup_set_default_group() -{ - errno_t sret; - char tmpstr[GPNAME_LEN]; - - cgutil_vaddr[TOPCG_ROOT]->used = 1; - cgutil_vaddr[TOPCG_ROOT]->gid = TOPCG_ROOT; - cgutil_vaddr[TOPCG_ROOT]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_ROOT]->grpname, GPNAME_LEN, GSCGROUP_ROOT, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = 100 * DEFAULT_IO_WEIGHT / MAX_IO_WEIGHT; - cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = DEFAULT_IO_WEIGHT; - cgutil_vaddr[TOPCG_ROOT]->percent = 1000; - - /* set root group as default cpu set */ - (void)sprintf_s(cgutil_vaddr[TOPCG_ROOT]->cpuset, CPUSET_LEN, "%s", cgutil_allset); - - cgutil_vaddr[TOPCG_BACKEND]->used = 1; - cgutil_vaddr[TOPCG_BACKEND]->gid = TOPCG_BACKEND; - cgutil_vaddr[TOPCG_BACKEND]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_BACKEND]->grpname, GPNAME_LEN, GSCGROUP_TOP_BACKEND, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = TOP_BACKEND_PERCENT; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_BACKEND_PERCENT / 10; - cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_BACKEND_PERCENT); - cgutil_vaddr[TOPCG_BACKEND]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_BACKEND_PERCENT / 100; - - /* set root group as gaussdb group cpu set */ - if (*cgutil_vaddr[TOPCG_BACKEND]->cpuset == '\0') - (void)sprintf_s(cgutil_vaddr[TOPCG_BACKEND]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); - - cgutil_vaddr[TOPCG_CLASS]->used = 1; - cgutil_vaddr[TOPCG_CLASS]->gid = TOPCG_CLASS; - cgutil_vaddr[TOPCG_CLASS]->gtype = GROUP_TOP; - sret = strncpy_s(cgutil_vaddr[TOPCG_CLASS]->grpname, GPNAME_LEN, GSCGROUP_TOP_CLASS, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = TOP_CLASS_PERCENT; - cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10; - cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT); - cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100; - /* set root group as gaussdb group cpu set */ - if (*cgutil_vaddr[TOPCG_CLASS]->cpuset == '\0') - (void)sprintf_s(cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset); - - cgutil_vaddr[BACKENDCG_START_ID]->used = 1; - cgutil_vaddr[BACKENDCG_START_ID]->gid = BACKENDCG_START_ID; - cgutil_vaddr[BACKENDCG_START_ID]->gtype = GROUP_BAKWD; - cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.tgid = TOPCG_BACKEND; - cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.percent = DEFAULT_BACKEND_PERCENT; - sret = strncpy_s(cgutil_vaddr[BACKENDCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_BACKEND, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[BACKENDCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_BACKEND_PERCENT / 10; - cgutil_vaddr[BACKENDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_BACKEND_PERCENT); - cgutil_vaddr[BACKENDCG_START_ID]->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * DEFAULT_BACKEND_PERCENT / 100; - - /* set root group as backend group cpu set */ - if (*cgutil_vaddr[BACKENDCG_START_ID]->cpuset == '\0') - (void)sprintf_s( - cgutil_vaddr[BACKENDCG_START_ID]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_BACKEND]->cpuset); - - cgutil_vaddr[BACKENDCG_START_ID + 1]->used = 1; - cgutil_vaddr[BACKENDCG_START_ID + 1]->gid = BACKENDCG_START_ID + 1; - cgutil_vaddr[BACKENDCG_START_ID + 1]->gtype = GROUP_BAKWD; - cgutil_vaddr[BACKENDCG_START_ID + 1]->ginfo.cls.tgid = TOPCG_BACKEND; - cgutil_vaddr[BACKENDCG_START_ID + 1]->ginfo.cls.percent = VACUUM_PERCENT; - sret = strncpy_s(cgutil_vaddr[BACKENDCG_START_ID + 1]->grpname, GPNAME_LEN, GSCGROUP_VACUUM, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[BACKENDCG_START_ID + 1]->ainfo.shares = DEFAULT_CPU_SHARES * VACUUM_PERCENT / 10; - cgutil_vaddr[BACKENDCG_START_ID + 1]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, VACUUM_PERCENT); - cgutil_vaddr[BACKENDCG_START_ID + 1]->percent = cgutil_vaddr[TOPCG_BACKEND]->percent * VACUUM_PERCENT / 100; - /* set root group as backend group cpu set */ - if (*cgutil_vaddr[BACKENDCG_START_ID + 1]->cpuset == '\0') - (void)sprintf_s( - cgutil_vaddr[BACKENDCG_START_ID + 1]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_BACKEND]->cpuset); - - cgutil_vaddr[CLASSCG_START_ID]->used = 1; - cgutil_vaddr[CLASSCG_START_ID]->gid = CLASSCG_START_ID; - cgutil_vaddr[CLASSCG_START_ID]->gtype = GROUP_CLASS; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.tgid = TOPCG_CLASS; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.maxlevel = 1; /* initialized value */ - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.percent = DEFAULT_CLASS_PERCENT; - cgutil_vaddr[CLASSCG_START_ID]->ginfo.cls.rempct = 100; /* initialized value */ - sret = strncpy_s(cgutil_vaddr[CLASSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_CLASS, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[CLASSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * DEFAULT_CLASS_PERCENT / 10; - cgutil_vaddr[CLASSCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, DEFAULT_CLASS_PERCENT); - /* it has only this class, so it has all resource */ - cgutil_vaddr[CLASSCG_START_ID]->percent = cgutil_vaddr[TOPCG_CLASS]->percent; - - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].skewpercent = DEFAULT_CPUSKEWPCT; - cgutil_vaddr[CLASSCG_START_ID]->except[EXCEPT_PENALTY].qualitime = DEFAULT_QUALITIME; - - cgutil_vaddr[WDCG_START_ID]->used = 1; - cgutil_vaddr[WDCG_START_ID]->gid = WDCG_START_ID; - cgutil_vaddr[WDCG_START_ID]->gtype = GROUP_DEFWD; - cgutil_vaddr[WDCG_START_ID]->ginfo.wd.cgid = CLASSCG_START_ID; - cgutil_vaddr[WDCG_START_ID]->ginfo.wd.wdlevel = 1; - sret = snprintf_s(tmpstr, sizeof(tmpstr), sizeof(tmpstr) - 1, "%s:%d", GSCGROUP_TOP_WORKLOAD, 1); - securec_check_intval(sret, , ); - - sret = strncpy_s(cgutil_vaddr[WDCG_START_ID]->grpname, GPNAME_LEN, tmpstr, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - - cgutil_vaddr[WDCG_START_ID]->ainfo.shares = MAX_CLASS_CPUSHARES * TOPWD_PERCENT / 100; - cgutil_vaddr[WDCG_START_ID]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOPWD_PERCENT); - - cgutil_vaddr[TSCG_START_ID]->used = 1; - cgutil_vaddr[TSCG_START_ID]->gid = TSCG_START_ID; - cgutil_vaddr[TSCG_START_ID]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID]->ginfo.ts.rate = TS_LOW_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_LOW_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID]->ainfo.shares = DEFAULT_CPU_SHARES * TS_LOW_RATE; - cgutil_vaddr[TSCG_START_ID]->ainfo.weight = MIN_IO_WEIGHT * TS_LOW_RATE; - - /* medium group of default group */ - cgutil_vaddr[TSCG_START_ID + 1]->used = 1; - cgutil_vaddr[TSCG_START_ID + 1]->gid = TSCG_START_ID + 1; - cgutil_vaddr[TSCG_START_ID + 1]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID + 1]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID + 1]->ginfo.ts.rate = TS_MEDIUM_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 1]->grpname, GPNAME_LEN, GSCGROUP_MEDIUM_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID + 1]->ainfo.shares = DEFAULT_CPU_SHARES * TS_MEDIUM_RATE; - cgutil_vaddr[TSCG_START_ID + 1]->ainfo.weight = MIN_IO_WEIGHT * TS_MEDIUM_RATE; - - /* high group of default group */ - cgutil_vaddr[TSCG_START_ID + 2]->used = 1; - cgutil_vaddr[TSCG_START_ID + 2]->gid = TSCG_START_ID + 2; - cgutil_vaddr[TSCG_START_ID + 2]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID + 2]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID + 2]->ginfo.ts.rate = TS_HIGH_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 2]->grpname, GPNAME_LEN, GSCGROUP_HIGH_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID + 2]->ainfo.shares = DEFAULT_CPU_SHARES * TS_HIGH_RATE; - cgutil_vaddr[TSCG_START_ID + 2]->ainfo.weight = MIN_IO_WEIGHT * TS_HIGH_RATE; - - /* rush group of default group */ - cgutil_vaddr[TSCG_START_ID + 3]->used = 1; - cgutil_vaddr[TSCG_START_ID + 3]->gid = TSCG_START_ID + 3; - cgutil_vaddr[TSCG_START_ID + 3]->gtype = GROUP_TSWD; - cgutil_vaddr[TSCG_START_ID + 3]->ginfo.ts.cgid = CLASSCG_START_ID; - cgutil_vaddr[TSCG_START_ID + 3]->ginfo.ts.rate = TS_RUSH_RATE; - sret = strncpy_s(cgutil_vaddr[TSCG_START_ID + 3]->grpname, GPNAME_LEN, GSCGROUP_RUSH_TIMESHARE, GPNAME_LEN - 1); - securec_check_errno(sret, , ); - cgutil_vaddr[TSCG_START_ID + 3]->ainfo.shares = DEFAULT_CPU_SHARES * TS_RUSH_RATE; - cgutil_vaddr[TSCG_START_ID + 3]->ainfo.weight = MIN_IO_WEIGHT * TS_RUSH_RATE; -} - -extern void cgconf_generate_default_config_file(void* vaddr); -extern int cgexec_update_remain_cgroup_cpuset(int cls, char* cpuset, unsigned char update); -extern int cgexec_check_cpuset_value(const char* clsset, const char* grpset); -extern int cgexec_update_class_cpuset(int cls, char* cpuset); -extern int cgexec_update_top_group_cpuset(int top, char* cpuset); -extern void cgexec_update_fixed_config(int high, int extended); - -void cgroup_unit_test_case() -{ - char* argv[] = {"gs_cgroup", "-D", "/dev/cgroups/test", "--upgrade"}; - int argc = sizeof(argv) / sizeof(*argv); - int sret = 0; - - cgutil_opt.mpflag = 1; - (void)cgexec_mount_root_cgroup(); - (void)cgexec_umount_root_cgroup(); - - (void)parse_options(argc, argv); - - for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { - if (NULL == (cgutil_vaddr[i] = (gscgroup_grp_t*)malloc(sizeof(gscgroup_grp_t)))) { - fprintf(stderr, "ERROR: failed to allocate memory for gsgroup!\n"); - for (int index = 0; index < i; ++index) { - free(cgutil_vaddr[index]); - cgutil_vaddr[index] = NULL; - } - return; - } - } - - cgroup_set_default_group(); - - for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { - free(cgutil_vaddr[i]); - cgutil_vaddr[i] = NULL; - } - gscgroup_grp_t vaddr[GSCGROUP_ALLNUM]; - - cgconf_generate_default_config_file(vaddr); - - sret = memset_s(&cgutil_opt, sizeof(cgutil_opt), 0, sizeof(cgutil_opt)); - securec_check_c(sret, "\0", "\0"); - - sret = snprintf_s(cgutil_opt.sets, sizeof(cgutil_opt.sets), sizeof(cgutil_opt.sets) - 1, "%s", "2-8"); - securec_check_ss_c(sret, "\0", "\0"); - - cgconf_set_class_group(1); - - sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "wg1"); - securec_check_ss_c(sret, "\0", "\0"); - - cgconf_set_class_group(1); - - cgconf_set_workload_group(1, 2); - - cgutil_opt.fixed = 1; - cgutil_is_sles11_sp2 = 0; - check_percentage_value(1, 1, 1, 1); - - cgutil_opt.display = 1; - cgutil_opt.user[0] = '\0'; - check_user_process(); - - cgutil_opt.display = 0; - cgutil_opt.ptree = 1; - check_user_process(); - - cgutil_opt.ptree = 0; - cgutil_opt.cflag = 1; - cgutil_opt.fixed = 0; - - sret = snprintf_s(cgutil_opt.topname, sizeof(cgutil_opt.topname), sizeof(cgutil_opt.topname) - 1, "%s", "Gaussdb"); - securec_check_ss_c(sret, "\0", "\0"); - - check_flag_process(); - - cgutil_opt.topname[0] = '\0'; - sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "wg1"); - securec_check_ss_c(sret, "\0", "\0"); - - check_flag_process(); - - sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "class1:wg1"); - securec_check_ss_c(sret, "\0", "\0"); - - check_flag_process(); - - cgutil_opt.cflag = 0; - cgutil_opt.dflag = 1; - sret = snprintf_s(cgutil_opt.topname, sizeof(cgutil_opt.topname), sizeof(cgutil_opt.topname) - 1, "%s", "Gaussdb"); - securec_check_ss_c(sret, "\0", "\0"); - check_flag_process(); - - cgutil_opt.topname[0] = '\0'; - sret = snprintf_s(cgutil_opt.user, sizeof(cgutil_opt.user), sizeof(cgutil_opt.user) - 1, "%s", "root"); - securec_check_ss_c(sret, "\0", "\0"); - check_user_name(); - - sret = snprintf_s(cgutil_opt.user, sizeof(cgutil_opt.user), sizeof(cgutil_opt.user) - 1, "%s", "xxx"); - securec_check_ss_c(sret, "\0", "\0"); - check_user_name(); - - cgutil_opt.user[0] = '\0'; - check_and_get_group_percent(NULL, "abc"); - - cgexec_check_cpuset_value("1-2", "3-4"); - - cgexec_update_remain_cgroup_cpuset(1, "3-4", 1); - cgexec_update_remain_cgroup_cpuset(1, "3-4", 0); - cgexec_update_class_cpuset(1, "3-4"); - - cgexec_update_top_group_cpuset(TOPCG_ROOT, "3-4"); - cgexec_update_top_group_cpuset(TOPCG_GAUSSDB, "3-4"); - cgexec_update_top_group_cpuset(TOPCG_BACKEND, "3-4"); - cgexec_update_top_group_cpuset(TOPCG_CLASS, "3-4"); - cgexec_update_top_group_cpuset(-1, "3-4"); - - cgutil_opt.mpflag = 1; - cgexec_check_mount_for_upgrade(); - - cgutil_opt.mpflag = 0; - - cgutil_opt.cflag = 0; - - cgutil_opt.cflag = 1; - sret = snprintf_s( - cgutil_opt.mpoints[0], sizeof(cgutil_opt.mpoints[0]), sizeof(cgutil_opt.mpoints[0]) - 1, "%s", "/dev/abc"); - securec_check_ss_c(sret, "\0", "\0"); - - cgexec_umount_root_cgroup(); - - sret = memset_s(cgutil_vaddr[CLASSCG_START_ID]->except, - EXCEPT_ALL_KINDS * sizeof(except_data_t), - 0, - EXCEPT_ALL_KINDS * sizeof(except_data_t)); - securec_check_c(sret, "\0", "\0"); - - cgexec_create_groups(); - - sret = snprintf_s(cgutil_opt.clsname, sizeof(cgutil_opt.clsname), sizeof(cgutil_opt.clsname) - 1, "%s", "class2"); - securec_check_ss_c(sret, "\0", "\0"); - cgconf_set_class_group(31); - - sret = snprintf_s(cgutil_opt.clsname, sizeof(cgutil_opt.clsname), sizeof(cgutil_opt.clsname) - 1, "%s", "class3"); - securec_check_ss_c(sret, "\0", "\0"); - cgconf_set_class_group(32); - - sret = snprintf_s(cgutil_opt.clsname, sizeof(cgutil_opt.clsname), sizeof(cgutil_opt.clsname) - 1, "%s", "class4"); - securec_check_ss_c(sret, "\0", "\0"); - cgconf_set_class_group(33); - - sret = snprintf_s(cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", "0-47"); - securec_check_ss_c(sret, "\0", "\0"); - - sret = snprintf_s(cgutil_vaddr[31]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", "0-46"); - securec_check_ss_c(sret, "\0", "\0"); - sret = snprintf_s(cgutil_vaddr[32]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", "0-30"); - securec_check_ss_c(sret, "\0", "\0"); - sret = snprintf_s(cgutil_vaddr[33]->cpuset, CPUSET_LEN, CPUSET_LEN - 1, "%s", "0-0"); - securec_check_ss_c(sret, "\0", "\0"); - - cgexec_update_fixed_config(TOPCG_CLASS, 0); - - cgconf_reset_class_group(31); - cgconf_reset_class_group(32); - cgconf_reset_class_group(33); -} -#endif -- 2.34.1 From 6962802cc9471e1ab31046dda782c05cee3d0d03 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:03:28 +0800 Subject: [PATCH 10/56] ADD file via upload --- src/bin/gs_cgroup/main.cpp | 1629 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1629 insertions(+) create mode 100644 src/bin/gs_cgroup/main.cpp diff --git a/src/bin/gs_cgroup/main.cpp b/src/bin/gs_cgroup/main.cpp new file mode 100644 index 000000000..58e2eac77 --- /dev/null +++ b/src/bin/gs_cgroup/main.cpp @@ -0,0 +1,1629 @@ +/* + * 版权声明:Copyright (c) 2020华为技术有限公司。 + * + * openGauss在Mulan PSL v2许可下发布。 + * 您可以根据Mulan PSL v2的条款和条件使用本软件。 + * 您可以在以下网址获得Mulan PSL v2的一份副本: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * 本软件按“原样”提供,没有任何明示或暗示的保证, + * 包括但不限于不侵权、适销性或特定用途适用性的保证。 + * 有关更多详情,请参阅Mulan PSL v2。 + * ------------------------------------------------------------------------- + * + * main.cpp + * gs_cgroup实用程序的主函数文件 + * + * 标识 + * src/bin/gs_cgroup/main.cpp + * + * ------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include + +#include + +#include "securec.h" +#include "cgutil.h" +#include "pg_config.h" +#include "getopt_long.h" + + /* 声明全局变量,描述Cgroup配置文件 */ +gscgroup_grp_t* cgutil_vaddr[GSCGROUP_ALLNUM] = { NULL }; + +/* 声明全局变量,存储gs_cgroup的选项 */ +cgutil_opt_t cgutil_opt = { 0 }; + +/* 声明变量,存储CPU的数量 */ +int cgutil_cpucnt = 0; + +/* 声明全局变量,指示Cgroup配置文件的用户 */ +struct passwd* cgutil_passwd_user = NULL; + +/* 声明全局变量,存储版本信息 */ +static char* cgutil_version = NULL; +/* 存储所有操作系统核心 */ +char cgutil_allset[CPUSET_LEN]; +/* 存储操作系统的内存集合 */ +char cgutil_mems[CPUSET_LEN]; + +char* current_nodegroup = NULL; + +#define MAX_PATH_LEN 1024 /* 文件路径的最大长度 */ +#define MAX_BUF_SIZE 2048 /* 缓冲区的最大大小 */ +#define STATIC_CONFIG_FILE "cluster_static_config" /* 集群静态配置文件的名称 */ +#define PROG_NAME "gs_cgroup" + +/* + * 函数名: usage + * 描述: gs_cgroup的使用方法函数 + * + */ +static void usage(void) +{ + fprintf(stdout, + "\ngs_cgroup用于管理每个节点上的Gauss Cgroup。\n" + "使用方法:\n gs_cgroup [选项]...\n\n" + "选项:\n" + " -a [--abort] : 中止异常标志,应与'-E数据'一起使用。\n" + " -b pct : 后端组的百分比\n" + " -B name : 与'-u'一起指定组名\n" + " -c : 创建默认的控制组,\n" + " 与'-S'和'-G'一起创建指定的类组和工作负载组;\n" + " 与'-N'一起创建指定逻辑集群的控制组。\n" + " -d : 删除所有控制组,与'-S'和'-G'一起删除指定的组\n" + " 与'-N'一起删除指定逻辑集群的控制组。\n" + " -E data : 数据在节点间迁移期间异常。数据值为[enable|disable]\n" + " enable表示在节点间迁移期间异常中断,并恢复到迁移前的状态。\n" + " disable表示在节点间迁移期间异常中断,系统不能恢复到迁移前的状态。\n" + " -E postprocess : 在进程附加到后端组后执行后处理。\n" + " -g : 获取Cgroup配置文件的路径\n" + " -G name : 指定类组\n" + " -l : 列出所有组\n" + " -L name : 指定逻辑集群\n" + " -m HashBucketNum : 分布式表的哈希桶数。\n" + " -M MaxDopVal : 替代openGauss实例的最大值的DOP值。\n" + " -n : 查询逻辑集群中的虚拟组。\n" + " -O name : 使用指定的日志文件存储信息。\n" + " -o name : 使用指定的日志文件进行输出。\n" + " -p : 重载配置文件。\n" + " -P : 打印所有组的配置文件。\n" + " -r : 重置工作负载组的配置。\n" + " -S : 使用分区分布列表。\n" + " -t : 指定为透传存储模式。\n" + " -U : 查询工作负载组的配置。\n" + " -v : 显示程序的版本信息。\n" + " -w name : 指定工作负载组名。\n" + " -x name : 删除指定组。\n" + " --help : 显示该帮助信息。\n" + " --check-env : 检查后端环境变量。\n" + " --node-group : 切换到指定的逻辑集群。\n" + " --print-group : 打印逻辑集群中的虚拟组列表。\n" + " --static-check : 检查静态配置文件。\n" + " --set-log-level= : 设置指定的模块名称的日志级别。\n" + " --set-log-file= : 设置指定的模块名称的日志文件。\n" + " --query-log-level : 查询日志级别。\n" + " --print-logfile : 打印日志文件。\n\n"); + exit(0); +} + +/* + * 功能: 检查后端环境变量是否有效 + * 参数: input_env_value - 环境变量值 + * 返回值: 成功返回0,失败返回1 + */ +extern int CheckBackendEnv(const char* input_env_value); + +int main(int argc, char** argv) +{ + int opt = 0; + int option_index = 0; + char* saveptr = NULL; + struct option long_options[] = { /* 参数选项列表 */ + {"abort", required_argument, NULL, 'a'}, /* 中止异常标志 */ + {"backend-group-percentage", required_argument, NULL, 'b'}, /* 后端组的百分比 */ + {"backend-group-name", required_argument, NULL, 'B'}, /* 后端组名称 */ + {"create", no_argument, NULL, 'c'}, /* 创建默认的控制组 */ + {"drop", no_argument, NULL, 'd'}, /* 删除所有控制组 */ + {"enable-data-abort", required_argument, NULL, 'E'}, /* 数据在节点间迁移期间异常 */ + {"postprocess", no_argument, NULL, 'E'}, /* 进程附加到后端组后执行后处理 */ + {"get_cfgpath", no_argument, NULL, 'g'}, /* 获取Cgroup配置文件的路径 */ + {"subclass-name", required_argument, NULL, 'G'}, /* 指定类组名称 */ + {"list", no_argument, NULL, 'l'}, /* 列出所有组 */ + {"logic-cluster-name", required_argument, NULL, 'L'}, /* 指定逻辑集群名称 */ + {"hash-bucket-number", required_argument, NULL, 'm'}, /* 分布式表的哈希桶数 */ + {"max-dop-value", required_argument, NULL, 'M'}, /* 替代openGauss实例的最大值的DOP值 */ + {"get_virtgroup_list", no_argument, NULL, 'n'}, /* 查询逻辑集群中的虚拟组 */ + {"log-sto-opt", required_argument, NULL, 'O'}, /* 使用指定的日志文件存储信息 */ + {"log-opt", required_argument, NULL, 'o'}, /* 使用指定的日志文件进行输出 */ + {"reload-cfg", no_argument, NULL, 'p'}, /* 重新加载配置文件 */ + {"print_cfg", no_argument, NULL, 'P'}, /* 打印所有组的配置文件 */ + {"reset-wlgcfg", no_argument, NULL, 'r'}, /* 重置工作负载组的配置 */ + {"use-distribution-list", no_argument, NULL, 'S'}, /* 使用分区分布列表 */ + {"use-trans-storage", no_argument, NULL, 't'}, /* 指定为透传存储模式 */ + {"get_wlgcfg", no_argument, NULL, 'U'}, /* 查询工作负载组的配置 */ + {"version", no_argument, NULL, 'v'}, /* 显示程序的版本信息 */ + {"wlgname", required_argument, NULL, 'w'}, /* 指定工作负载组名 */ + {"delgroupname", required_argument, NULL, 'x'}, /* 删除指定组 */ + {"help", no_argument, NULL, 'h'}, /* 显示帮助信息 */ + {"check-env", no_argument, NULL, 1}, /* 检查后端环境变量 */ + {"node-group", required_argument, NULL, 2}, /* 切换到指定的逻辑集群 */ + {"print-group", no_argument, NULL, 3}, /* 打印逻辑集群中的虚拟组列表 */ + {"static-check", no_argument, NULL, 4}, /* 检查静态配置文件 */ + {"set-log-level", required_argument, NULL, 5}, /* 设置指定模块名称的日志级别 */ + {"set-log-file", required_argument, NULL, 6}, /* 设置指定模块名称的日志文件 */ + {"query-log-level", no_argument, NULL, 7}, /* 查询日志级别 */ + {"print-logfile", no_argument, NULL, 8}, /* 打印日志文件 */ + {NULL, 0, NULL, 0} + }; + + /* 解析命令行参数 */ + while ((opt = getopt_long(argc, argv, "a:b:B:cdE:gG:lL:m:M:nO:o:pPrStUvw:x:h", + long_options, &option_index)) != -1) { + switch (opt) { + case 'a': /* 中止异常标志 */ + break; + case 'b': /* 后端组的百分比 */ + break; + case 'B': /* 后端组名称 */ + break; + case 'c': /* 创建默认的控制组 */ + break; + case 'd': /* 删除所有控制组 */ + break; + case 'E': /* 数据在节点间迁移期间异常 */ + break; + case 'g': /* 获取Cgroup配置文件的路径 */ + break; + case 'G': /* 指定类组名称 */ + break; + case 'l': /* 列出所有组 */ + break; + case 'L': /* 指定逻辑集群名称 */ + break; + case 'm': /* 分布式表的哈希桶数 */ + break; + case 'M': /* 替代openGauss实例的最大值的DOP值 */ + break; + case 'n': /* 查询逻辑集群中的虚拟组 */ + break; + case 'O': /* 使用指定的日志文件存储信息 */ + break; + case 'o': /* 使用指定的日志文件进行输出 */ + break; + case 'p': /* 重新加载配置文件 */ + break; + case 'P': /* 打印所有组的配置文件 */ + break; + case 'r': /* 重置工作负载组的配置 */ + break; + case 'S': /* 使用分区分布列表 */ + break; + case 't': /* 指定为透传存储模式 */ + break; + case 'U': /* 查询工作负载组的配置 */ + break; + case 'v': /* 显示程序的版本信息 */ + break; + case 'w': /* 指定工作负载组名 */ + break; + case 'x': /* 删除指定组 */ + break; + case 'h': /* 显示帮助信息 */ + break; + case 1: /* 检查后端环境变量 */ + break; + case 2: /* 切换到指定的逻辑集群 */ + break; + case 3: /* 打印逻辑集群中的虚拟组列表 */ + break; + case 4: /* 检查静态配置文件 */ + break; + case 5: /* 设置指定模块名称的日志级别 */ + break; + case 6: /* 设置指定模块名称的日志文件 */ + break; + case 7: /* 查询日志级别 */ + break; + case 8: /* 打印日志文件 */ + break; + default: + usage(); + break; + } + } + + return 0; +} +/* + * @Description: 检查不同组和cpusets的百分比。 + * @IN bkd: 检查是否更新后端组 + * @IN grp: 检查是否更新工作负载组 + * @IN cls: 检查是否更新类别组 + * @IN top: 检查是否更新顶级组 + * @Return: -1:异常 0:正常 + * @See also: + * 该函数的功能是检查不同组和cpusets的百分比值。函数接受四个参数:bkd、grp、cls和top,用于检查后端组、工作负载组、类别组和顶级组是否正在更新。函数返回值为 - 1表示异常,返回值为0表示正常。 +详细解释: +1. 如果cgutil_opt.fixed为真,表示进入了fixed模式。 +2. 在fixed模式下: +a.如果bkd、grp、cls和top的和大于1,说明指定了多个组的百分比值,这是不允许的,会返回错误信息并返回 - 1。 +b.如果bkd、grp、cls和top的和为0,说明没有指定任何组的百分比值,直接返回0。 +c.调用check_group_name_redundant函数,检查组名是否重复。 +d.检查后端百分比值,范围为1 - 100。 +e.检查组百分比值,范围为1 - 100。 +f.检查类别百分比值,范围为1 - 100,并将clssetpct设置为1。 +g.检查顶级组百分比值,范围为1 - 100。 +h.如果用户设置的核心百分比为0,设置setfixed标志为1。 +3. 如果cgutil_opt.fixed为假,表示进入了非fixed模式。 +a.检查后端百分比值,范围为1 - 99。 +b.检查组百分比值,范围为1 - 99。 +c.检查类别百分比值,范围为1 - 99。 +d.检查顶级组百分比值,范围为1 - 99。 +函数最后返回0表示正常。 + */ +static int check_percentage_value(int bkd, int grp, int cls, int top) +{ + /* fixed模式 */ + if (cgutil_opt.fixed) { + /* + * 当通过百分比更新cpuset时,不允许指定多个组百分比 + */ + if (bkd + cls + top + grp > 1) { + fprintf(stderr, "ERROR: 冗余的CPU核心百分比选项。\n"); + return -1; + } + else if (bkd + cls + top + grp == 0) { + return 0; + } + + check_group_name_redundant(bkd, grp, cls, top); + + /* 检查后端百分比,cpuset百分比范围是1-100 */ + if (cgutil_opt.uflag && bkd) { + if (cgutil_opt.bkdpct > 100 || cgutil_opt.bkdpct < 0) { + fprintf(stderr, + "ERROR: 无效的CPU核心百分比值。它的范围应为0-100。\n"); + return -1; + } + cgutil_opt.setspct = cgutil_opt.bkdpct; + cgutil_opt.bkdpct = 0; + } + + /* 检查组百分比 */ + if (cgutil_opt.uflag && grp) { + if (cgutil_opt.grppct > 100 || cgutil_opt.grppct < 0) { + fprintf(stderr, + "ERROR: 无效的CPU核心百分比值。它的范围应为0-100。\n"); + return -1; + } + cgutil_opt.setspct = cgutil_opt.grppct; + cgutil_opt.grppct = 0; + } + + /* 检查类别百分比 */ + if (cgutil_opt.uflag && cls) { + if (cgutil_opt.clspct > 100 || cgutil_opt.clspct < 0) { + fprintf(stderr, + "ERROR: 无效的CPU核心百分比值。它的范围应为0-100。\n"); + return -1; + } + cgutil_opt.setspct = cgutil_opt.clspct; + cgutil_opt.clspct = 0; + cgutil_opt.clssetpct = 1; + } + + /* 检查顶级组百分比 */ + if (cgutil_opt.uflag && top) { + if (cgutil_opt.toppct > 100 || cgutil_opt.toppct < 0) { + fprintf(stderr, + "ERROR: 无效的CPU核心百分比值。它的范围应为0-100。\n"); + return -1; + } + cgutil_opt.setspct = cgutil_opt.toppct; + cgutil_opt.toppct = 0; + } + + // 如果用户设置的核心百分比为0,则设置一个标志以显示用户设置 + if (cgutil_opt.setspct == 0) + cgutil_opt.setfixed = 1; + } + else { + if ((cgutil_opt.cflag || cgutil_opt.uflag) && bkd && (cgutil_opt.bkdpct >= 100 || cgutil_opt.bkdpct < 1)) { + fprintf(stderr, + "ERROR: 后端组动态百分比的值无效。范围应为1~99!\n"); + return -1; + } + + /* 检查后端百分比 */ + if ((cgutil_opt.cflag || cgutil_opt.uflag) && grp && (cgutil_opt.grppct >= 100 || cgutil_opt.grppct < 1)) { + fprintf(stderr, + "ERROR: 工作负载组动态百分比的值无效。范围应为1~99!\n"); + return -1; + } + + /* 检查组百分比 */ + if ((cgutil_opt.cflag || cgutil_opt.uflag) && cls && (cgutil_opt.clspct >= 100 || (cgutil_opt.clspct < 1))) { + fprintf(stderr, + "ERROR: 类别组动态百分比的值无效。范围应为1~99!\n"); + return -1; + } + + /* 检查类别百分比 */ + if ((cgutil_opt.cflag || cgutil_opt.uflag) && top && (cgutil_opt.toppct >= 100 || cgutil_opt.toppct < 1)) { + fprintf(stderr, + "ERROR: 顶级组动态百分比的值无效。范围应为1~99!\n"); + return -1; + } + } + + return 0; +} +/* + * @Description: 检查节点组是否有效。 + * @Return: -1:异常,0:正常 + * @See also: + * 函数check_node_group_name用于检查节点组名称是否有效。首先获取静态配置文件,然后通过环境变量获取GAUSSHOME的值,并检查其有效性。接着根据一系列参数拼接出配置文件的路径path,最后检查文件是否可访问。如果文件不存在,则返回异常;否则返回正常。 + * 函数可以应用于配置管理系统中对节点组名称进行验证的场景。例如,在配置管理系统中创建或修改节点组时,可以调用该函数来验证节点组名称的合法性,以确保配置信息的正确性。 + */ +static int check_node_group_name() { + char path[MAX_PATH_LEN]; + struct stat stat_buf; + + /* 获取静态配置文件 */ + errno_t sret; + sret = memset_s(&stat_buf, sizeof(stat_buf), 0, sizeof(stat_buf)); // 将stat_buf结构体初始化为0 + securec_check_errno(sret, , -1); + + char* exec_path = gs_getenv_r("GAUSSHOME"); // 获取环境变量GAUSSHOME的值 + if (NULL == exec_path) { + fprintf(stderr, "ERROR: Get GAUSSHOME failed, please check.\n"); // 打印错误消息 + return -1; + } + if (CheckBackendEnv(exec_path) != 0) { + return -1; + } + sret = snprintf_s(path, + MAX_PATH_LEN, + MAX_PATH_LEN - 1, + "%s/%s/%s_%s%s", + exec_path, + GSCGROUP_CONF_DIR, + GSCFG_PREFIX, + cgutil_passwd_user->pw_name, + GSCFG_SUFFIX); + securec_check_intval(sret, , -1); + + /* 检查文件是否可访问 */ + if (stat(path, &stat_buf) != 0) { // 检查文件是否存在 + fprintf(stderr, "ERROR: 文件 %s 不存在。\n", path); + return -1; + } + + return 0; +} + +/* + * @Description: 检查输入是否安全 + * @IN input: 输入字符串 + * @Return: void + * @See also: + * 函数check_input_for_security用于检查输入的字符串是否安全。它定义了一个危险字符数组danger_token,然后遍历数组,逐个检查输入中是否存在危险字符。如果存在,则输出错误消息并退出程序。 + * check_input_for_security函数可以应用于任何需要验证用户输入的场景。例如,在用户登录系统时,可以调用该函数来检查用户输入的用户名和密码是否包含危险字符,以增加系统的安全性。 + */ +static void check_input_for_security(char* input) { + char* danger_token[] = { "|", ";", "&", "$", "<", ">", "`", "\\", "!", "\n", NULL }; + + for (int i = 0; danger_token[i] != NULL; ++i) { + if (strstr(input, danger_token[i]) != NULL) { // 检查输入中是否存在危险字符 + printf("invalid token \"%s\"\n", danger_token[i]); // 打印错误消息 + exit(1); + } + } +} + +/* + * @Description: 检查类、类组和工作负载组名称是否有效 + * @IN void + * @Return: -1:异常,0:正常 + * @See also: + * 函数check_name_valid用于检查类、类组和工作负载组的名称是否有效。首先定义了最大名称长度namelen,然后检查类名称和类异常数据。如果类名为空且异常数据有效,则将类名设置为默认值。接着检查类名称长度是否超出指定大小,如果超出则返回异常。然后检查工作负载组名称长度是否超出指定大小,如果超出则返回异常。最后返回正常。 + * check_name_valid函数可以应用于数据库管理系统中对类、类组和工作负载组名称进行验证的场景。例如,在数据库管理系统中创建或修改类组和工作负载组时,可以调用该函数来验证名称的合法性,以确保系统的稳定性和安全性。 + */ +static int check_name_valid(void) { + int namelen = GPNAME_LEN / 2 - 1; // 最大名称长度 + errno_t sret; + + /* 检查类名称和类异常数据 */ + if (*cgutil_opt.clsname == '\0' && *cgutil_opt.edata) { + fprintf(stdout, + "NOTICE: 若未指定类名但异常数据有效,则类名将为 \"%s\"!\n", + GSCGROUP_DEFAULT_CLASS); + sret = snprintf_s(cgutil_opt.clsname, GPNAME_LEN, GPNAME_LEN - 1, "%s", GSCGROUP_DEFAULT_CLASS); // 将类名设置为默认值 + securec_check_intval(sret, , -1); + } + + /* 检查类名称长度 */ + if (strlen(cgutil_opt.clsname) > (size_t)namelen) { + *cgutil_opt.clsname = '\0'; + fprintf(stderr, + "ERROR: 类组名称超出了其指定大小 %d 字节。\n", + namelen); + + return -1; + } + + /* 检查工作负载组名称长度 */ + if (strlen(cgutil_opt.wdname) > (size_t)namelen - 3) { + *cgutil_opt.wdname = '\0'; + fprintf(stderr, + "ERROR: 工作负载组名称超出了其指定大小 %d 字节。\n", + namelen - 3); + + return -1; + } + + return 0; +} +/* + * @Description: 检查输入值的有效性。 + * @IN void + * @Return: -1: 异常 0: 正常 + * @See also: + */ +static int check_input_valid(void) +{ + /* 检查带有'--fixed'标志的组名 */ + if (*cgutil_opt.clsname == '\0' && *cgutil_opt.wdname == '\0' && *cgutil_opt.bkdname == '\0' && + *cgutil_opt.topname == '\0' && cgutil_opt.fixed) { + fprintf(stderr, "ERROR: 请使用\"--fixed\"标志指定一个组名\n"); + return -1; + } + + /* 检查'--fixed'和'-u'标志 */ + if (cgutil_opt.fixed && 0 == cgutil_opt.uflag) { + fprintf(stderr, "ERROR: 请同时使用\'--fixed\'标志和\'-u\'标志\n"); + return -1; + } + + /* 检查带有'-f'标志的组名 */ + if ((*cgutil_opt.clsname || *cgutil_opt.wdname || *cgutil_opt.bkdname || + (*cgutil_opt.topname && + (0 != strncmp(cgutil_opt.topname, GSCGROUP_TOP_DATABASE, sizeof(GSCGROUP_TOP_DATABASE))))) && + *cgutil_opt.sets) { + fprintf(stderr, "ERROR: 仅能在Gaussdb Group上指定\'-f\'选项\n"); + return -1; + } + + /* 用户不能同时使用'-f'和'--fixed'标志 */ + if (cgutil_opt.fixed && *cgutil_opt.sets) { + fprintf(stderr, "ERROR: 请从\'-f\'、\'--fixed\'中选择一个选项\n"); + return -1; + } + + /* 获取当前挂载点 */ + if (cgexec_get_mount_points() < 0) { + return -1; + } + + /* 检查'-c'、'-d'、'-u'标志 */ + if ((cgutil_opt.cflag && cgutil_opt.dflag) || (cgutil_opt.cflag && cgutil_opt.uflag) || + (cgutil_opt.uflag && cgutil_opt.dflag)) { + fprintf(stderr, "ERROR: 请只指定一个选项:'-c'、'-d'和'-u'\n"); + return -1; + } + + /* 检查'-e'标志 */ + if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_ERROR)) { + fprintf(stderr, "ERROR: 不能同时指定中止和处罚标志!\n"); + return -1; + } + + /* 检查'-e'标志的异常数据 */ + if (cgutil_opt.clsname[0] == '\0' && IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_PENALTY)) { + fprintf(stderr, "ERROR: 你必须指定具有处罚的类名!\n"); + return -1; + } + + /* 在没有'--penalty'、'--abort'和'-a'标志的情况下设置默认的异常数据 */ + if (*cgutil_opt.edata && IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) { + cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_PENALTY); + fprintf(stdout, "NOTICE: 如果不指定异常操作,默认为处罚!\n"); + } + + /* 检查'--refresh'、'--revert'和'--recover'标志 */ + if ((cgutil_opt.cflag || cgutil_opt.dflag || cgutil_opt.uflag) && + (cgutil_opt.refresh || cgutil_opt.revert || cgutil_opt.recover)) { + fprintf(stderr, + "ERROR: 不能在'-c'、'-u'或'-d'选项中同时指定'--refresh'、'--revert'或'--recover'!\n"); + return -1; + } + + /* 检查'--recover'标志 */ + if ((geteuid() == 0) && cgutil_opt.recover) { + fprintf(stderr, "ERROR: root用户不能指定'--recover'选项!\n"); + return -1; + } + + return 0; +} + +/* + * @Description: 检查带有标志的用户信息。 + * @IN void + * @Return: -1: 异常 0: 正常 + * @See also: + */ +static int check_user_process(void) +{ + /* 检查root用户进程 */ + if ((geteuid() == 0) && ((cgutil_opt.cflag || cgutil_opt.display || cgutil_opt.uflag || cgutil_opt.dflag) && + cgutil_opt.user[0] == '\0')) { + fprintf(stderr, + "ERROR: 在作为root用户运行时,必须使用'-c'、'-d'、'-p'或'-u'指定用户名\n"); + return -1; + } + + /* 检查非root用户进程 */ + if (geteuid() && cgutil_opt.user[0] != '\0') { + fprintf(stderr, "ERROR: 在以非root用户身份运行时,不能指定用户名\n"); + return -1; + } + + /* 检查'-P'标志的用户信息 */ + if (0 == geteuid() && cgutil_opt.ptree && '\0' == *cgutil_opt.user) { + fprintf(stderr, + "ERROR: 当以root用户身份运行时,必须指定用户名来显示cgroup树\n"); + return -1; + } + + /* 检查'-M'标志的非root用户信息 */ + if ((cgutil_opt.mflag || cgutil_opt.umflag) && geteuid()) { + fprintf(stderr, "ERROR: 必须以root用户身份运行才能挂载或卸载cgroup!\n"); + return -1; + } + + return 0; +} +/* + * @Description: 检查所有标志位。 + * @IN void + * @Return: -1: 异常 0: 正常 + * @See also: + */ +static int check_flag_process(void) +{ + /* create flag process */ + // 创建标志位处理 + if (cgutil_opt.cflag) { + /* check top and backend group name */ + // 检查顶层组和后端组名称 + if (cgutil_opt.topname[0] != '\0' || cgutil_opt.bkdname[0] != '\0') { + fprintf(stderr, + "错误:在创建cgroup期间,不能指定顶层组或后端组!\n"); + return -1; + } + + /* check workload group name and class name */ + // 检查工作负载组名和类名 + if (cgutil_opt.wdname[0] != '\0' && cgutil_opt.clsname[0] == '\0') { + fprintf(stderr, + "错误:在创建cgroup期间,不能仅指定组名而不指定类名!\n"); + return -1; + } + + if (*cgutil_opt.wdname && NULL != strchr(cgutil_opt.wdname, ':')) { + fprintf(stderr, "错误,工作负载组名不能包含':'字符。\n"); + return -1; + } + } + + /* delete flag process */ + // 删除标志位处理 + if (cgutil_opt.dflag) { + /* check top and backend group name */ + // 检查顶层组和后端组名称 + if (cgutil_opt.topname[0] != '\0' || cgutil_opt.bkdname[0] != '\0') { + fprintf(stderr, + "错误:在删除cgroup期间,不能指定顶层组或后端组!\n"); + return -1; + } + } + + /* update flag process */ + // 更新标志位处理 + if (cgutil_opt.uflag && + ('\0' == cgutil_opt.topname[0] && '\0' == cgutil_opt.bkdname[0] && '\0' == cgutil_opt.clsname[0])) { + fprintf(stderr, "错误:在更新cgroup时,请指定组名!\n"); + return -1; + } + return 0; +} + +/** + * 该函数检查所有标志位的状态。 + * @param void + * @return int:-1表示异常,0表示正常 + * + * 例如,当创建cgroup时,需要检查一些条件: + * - 不能指定顶层组或后端组的名称 + * - 不能仅指定组名而不指定类名 + * - 工作负载组名不能包含冒号字符 + * 如果满足以上条件,则返回0表示正常,否则返回-1表示异常。 + */ + + /* + * @Description: check group names. + * @IN void + * @Return: -1: abnormal 0: normal + * @See also: + */ +static int check_group_name_process(int top, int bkd) +{ + /* check class name and percentage */ + // 检查类名和百分比 + if (cgutil_opt.clspct && '\0' == cgutil_opt.clsname[0]) { + fprintf(stderr, + "错误:请同时指定类名和百分比!\n"); + return -1; + } + + /* check workload group name and percentage */ + // 检查工作负载组名和百分比 + if (cgutil_opt.grppct && '\0' == cgutil_opt.wdname[0]) { + fprintf(stderr, + "错误:请同时指定工作负载名和百分比!\n"); + return -1; + } + + /* workload group name special process */ + // 特殊处理工作负载组名 + if (cgutil_opt.wdname[0] != '\0') { + if ((NULL == strchr(cgutil_opt.wdname, ':') && 0 == strcmp(cgutil_opt.wdname, GSCGROUP_TOP_WORKLOAD)) || + (NULL != strchr(cgutil_opt.wdname, ':') && + 0 == strncmp(cgutil_opt.wdname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1))) { + fprintf(stderr, "错误:不能对%s组执行任何操作!\n", GSCGROUP_TOP_WORKLOAD); + return -1; + } + } + ... +} + +/** + * 该函数检查组名的状态。 + * @param top:顶层组名 + * @param bkd:后端组名 + * @return int:-1表示异常,0表示正常 + * + * 例如,当检查组名时,需要检查一些条件: + * - 如果指定了百分比,则必须同时指定类名 + * - 如果指定了百分比,则必须同时指定工作负载名 + * - 特殊处理工作负载组名,禁止对特定组进行操作 + * 如果满足以上条件,则返回0表示正常,否则返回-1表示异常。 + */ + /* + * @Description: 检查组名。 + * @IN void + * @Return: -1:异常 0:正常 + * @See also: + * 该函数主要用于检查和处理组名以及相关参数的合法性,确保输入符合规定。在实际应用中,可以用于配置管理系统中对组名的检查和处理。例如,一个资源管理系统中,需要对组名进行检查和处理,以确保组名的唯一性和合法性。如果组名为空或与已有的组名重复,则会提示错误。 + */ +static int check_group_name_process(int top, int bkd) { + /* 检查班级名和百分比 */ + if (cgutil_opt.clspct && '\0' == cgutil_opt.clsname[0]) { + fprintf(stderr, + "ERROR: 请同时指定班级名和班级百分比!\n"); + return -1; + } + + /* 检查工作负载组名和百分比 */ + if (cgutil_opt.grppct && '\0' == cgutil_opt.wdname[0]) { + fprintf(stderr, + "ERROR: 请同时指定工作负载名和工作负载百分比!\n"); + return -1; + } + + /* 特殊处理工作负载组名 */ + if (cgutil_opt.wdname[0] != '\0') { + if ((NULL == strchr(cgutil_opt.wdname, ':') && 0 == strcmp(cgutil_opt.wdname, GSCGROUP_TOP_WORKLOAD)) || + (NULL != strchr(cgutil_opt.wdname, ':') && + 0 == strncmp(cgutil_opt.wdname, GSCGROUP_TOP_WORKLOAD, sizeof(GSCGROUP_TOP_WORKLOAD) - 1))) { + fprintf(stderr, "ERROR: 无法对 %s 组进行任何操作!\n", GSCGROUP_TOP_WORKLOAD); + return -1; + } + } + + /* 检查TimeShare组名 */ + if (cgutil_opt.wdname[0] && (0 == strcmp(cgutil_opt.wdname, GSCGROUP_RUSH_TIMESHARE) || + 0 == strcmp(cgutil_opt.wdname, GSCGROUP_HIGH_TIMESHARE) || + 0 == strcmp(cgutil_opt.wdname, GSCGROUP_MEDIUM_TIMESHARE) || + 0 == strcmp(cgutil_opt.wdname, GSCGROUP_LOW_TIMESHARE))) { + fprintf(stderr, + "ERROR: 不能将工作负载组名与默认的TimeShare组名相同!\n"); + return -1; + } + + /* 特殊处理top组名 */ + if (cgutil_opt.topname[0] != '\0') { + if (!cgutil_opt.uflag) { + fprintf(stderr, "ERROR: 使用top名时,请指定'-u'选项!\n"); + return -1; + } + + /* 检查使用top名时的百分比与'-f'标志 */ + if (!cgutil_opt.fixed && !*cgutil_opt.sets && !cgutil_opt.toppct) { + fprintf(stderr, "ERROR: 使用top名时,请指定动态top百分比!\n"); + return -1; + } + else if (cgutil_opt.fixed && + !(cgutil_opt.setspct || cgutil_opt.toppct || top)) { + fprintf(stderr, + "ERROR: 当更新固定值时,请指定cpu核心百分比或IO值!\n"); + return -1; + } + } + + /* 特殊处理backend组名 */ + if (cgutil_opt.bkdname[0] != '\0') { + if (!cgutil_opt.uflag) { + fprintf(stderr, + "ERROR: 使用backend名时,请指定'-u'选项!\n"); + return -1; + } + + /* 检查backend名 */ + if (!*cgutil_opt.sets && !cgutil_opt.toppct && !bkd) { + fprintf(stderr, + "ERROR: 当更新固定值时,请指定cpu核心百分比或IO值!\n"); + return -1; + } + } +} +/** + * @Description: 检查用户名称是否有效。 + * @IN void + * @Return: -1: 异常 0: 正常 + * @See also: + */ +static int check_user_name(void) +{ + /* 用户名称有效 */ + if (cgutil_opt.user[0]) { + /* 检查用户名称是否为root */ + if (0 == strcmp(cgutil_opt.user, "root")) { + fprintf(stderr, "ERROR: 不能将用户名称指定为root。\n"); + return -1; + } + else /* 获取用户ID和组ID */ + { + cgutil_passwd_user = getpwnam(cgutil_opt.user); + if (NULL == cgutil_passwd_user) { + fprintf(stderr, + "ERROR: 无法获取%s的UID和GID。\n" + "HINT: 请检查指定的用户名。\n", + cgutil_opt.user); + return -1; + } + } + } + else { + /* 保存当前用户信息 */ + cgutil_passwd_user = getpwuid(geteuid()); + if (NULL == cgutil_passwd_user) { + fprintf(stderr, + "ERROR: 无法获取cgutil_passwd_user。\n" + "HINT: 请检查正在运行的用户!\n"); + return -1; + } + } + + return 0; +} + +/** + * @Description: 检查所有的输入是否有效。 + * @IN bkd: 后端组ID + * @IN grp: 组ID + * @IN cls: 类ID + * @IN top: 顶级组ID + * @Return: -1: 异常 0: 正常 + * @See also: + */ +static int check_input_isvalid(int bkd, int grp, int cls, int top) +{ + /* 检查列表 */ + if (check_name_valid() == -1 || check_input_valid() == -1 || check_user_name() == -1 || + check_percentage_value(bkd, grp, cls, top) == -1 || check_group_name_process(top, bkd) == -1 || + check_user_process() == -1 || check_flag_process() == -1) { + return -1; + } + + return 0; +} + +/** + * @Description: 检查cpuset的值是否有效。 + * @IN cpuset: cpuset的值 + * @Return: -1: 异常 0: 正常 + * @See also: + */ +static int check_cpuset_value_valid(char* cpuset) +{ + char* bad = NULL; + int a = -1; + int b = -1; + char* p = NULL; + + if (*cpuset == '-') { + fprintf(stderr, "ERROR: 请使用有效的值指定cpuset。\n"); + return -1; + } + + p = strchr(cpuset, '-'); + + /* 检查"cpuset"的值是否为a-b */ + if (p == NULL) { + a = (int)strtol(cpuset, &bad, 10); + if ((bad != NULL) && *bad) { + fprintf(stderr, "ERROR: 请使用\"a-b\"或\"a\"格式指定cpuset。\n"); + return -1; + } + + b = a; + } + else { + *p++ = '\0'; + + a = (int)strtol(cpuset, &bad, 10); + if ((bad != NULL) && *bad) { + fprintf(stderr, "ERROR: 请使用有效的值指定cpuset。\n"); + return -1; + } + + b = (int)strtol(p, &bad, 10); + if ((bad != NULL) && *bad) { + fprintf(stderr, "ERROR: 请使用有效的值指定cpuset。\n"); + return -1; + } + } + + if ((a < 0) || (b < 0) || (a > b) || (b >= cgutil_cpucnt)) { + fprintf(stderr, "ERROR: 请使用有效的值指定cpuset。\n"); + return -1; + } + + int rcs = sprintf_s(cgutil_opt.sets, CPUSET_LEN, "%d-%d", a, b); + securec_check_intval(rcs, , -1); + + return 0; +} +/* +注释分析: +check_user_name()函数的功能是检查用户名称是否有效。该函数没有输入参数,返回 - 1表示异常,返回0表示正常。用户名称的有效性包括以下几个方面: +- 检查用户名称是否为"root",如果是,则打印错误信息,并返回 - 1。 +- 如果用户名称不是"root",则获取用户ID和组ID。如果获取失败,打印错误信息,并返回 - 1。 +- 如果用户名称为空,则保存当前用户信息。如果保存失败,打印错误信息,并返回 - 1。 +check_input_isvalid()函数的功能是检查所有的输入参数是否有效。输入参数有bkd、grp、cls、top四个整数变量。返回 - 1表示异常,返回0表示正常。函数内部依次检查以下内容的有效性: +- 调用check_name_valid()函数检查名称的有效性。 +- 调用check_input_valid()函数检查输入的有效性。 +- 调用check_user_name()函数检查用户名称的有效性。 +- 调用check_percentage_value()函数检查百分比值的有效性。 +- 调用check_group_name_process()函数检查组名和进程的有效性。 +- 调用check_user_process()函数检查用户和进程的有效性。 +- 调用check_flag_process()函数检查标志位和进程的有效性。 +如果其中任何一个检查返回 - 1,则整个函数返回 - 1,表示异常;否则返回0,表示正常。 +check_cpuset_value_valid()函数的功能是检查cpuset的值是否有效。输入参数是一个字符指针cpuset,返回 - 1表示异常,返回0表示正常。该函数执行以下操作: +- 首先检查cpuset的第一个字符是否为'-',如果是,则打印错误信息,并返回 - 1。 +- 然后查找字符串中的'-'字符,如果没有找到,则将字符串转换为整数a,如果转换失败或者转换后的值不合法(如包含非数字字符),则打印错误信息,并返回 - 1。此时将a赋值给b。 +- 如果找到了'-'字符,则将字符串转换为整数a和b,如果转换失败或者转换后的值不合法(如包含非数字字符),则打印错误信息,并返回 - 1。 +- 最后检查a和b的值是否合法(大于等于0,且a小于等于b,且b小于cputil_cpucnt),如果不合法,则打印错误信息,并返回 - 1。 +- 如果以上检查都通过,则将有效的a和b转换为字符串,并存储在cgutil_opt.sets变量中,返回0表示正常。 +/* + * @Description: 检查配置标志位。 + * @IN void + * @Return: 1: 正常 0: 异常 + * @See also: + * 函数用于检查配置标志位,判断是否满足特定的条件。如果满足条件,则返回1,否则返回0。函数参数为空。 + */ +static int check_config_flag(void) +{ + if (cgutil_opt.cflag || (cgutil_opt.dflag && (*cgutil_opt.clsname || *cgutil_opt.nodegroup)) || cgutil_opt.uflag || + cgutil_opt.display || (*cgutil_opt.edata && *cgutil_opt.clsname) || cgutil_opt.refresh || cgutil_opt.upgrade || + cgutil_opt.revert || cgutil_opt.recover) + return 1; + + return 0; +} + +/* + * @Description: 初始化cgroup配置。 + * @IN void + * @Return: -1: 异常 0: 正常 + * @See also: + * 函数用于初始化cgroup配置。首先调用check_config_flag()函数检查配置标志位,如果满足条件,则根据不同的用户权限获取GAUSSHOME路径,并进行相应的检查和设置。然后调用cgconf_parse_config_file()函数解析配置文件。如果解析失败,则根据不同的情况输出相应的错误信息。函数参数为空。 + */ +static int initialize_cgroup_config(void) +{ + char* hpath = NULL; + errno_t sret; + + /* 检索配置文件信息;如果不存在,则创建一个 */ + if (check_config_flag() > 0) { + if (geteuid() == 0) { // 如果是root用户,则需要指定GAUSSHOME路径 + if ('\0' == cgutil_opt.hpath[0]) { + fprintf(stderr, + "ERROR: you need specify the GAUSSHOME path " + "when runing as root user!\n"); + return -1; + } + } + else { // 如果不是root用户,则从环境变量中获取GAUSSHOME路径 + if (NULL == (hpath = gs_getenv_r("GAUSSHOME"))) { + fprintf(stderr, "ERROR: environment variable $GAUSSHOME is not set!\n"); + return -1; + } + if (CheckBackendEnv(hpath) != 0) { // 检查后端环境 + return -1; + } + sret = snprintf_s(cgutil_opt.hpath, sizeof(cgutil_opt.hpath), sizeof(cgutil_opt.hpath) - 1, "%s", hpath); + securec_check_intval(sret, , -1); + } + + if (-1 == cgconf_parse_config_file()) { // 解析配置文件 + if (cgutil_opt.dflag && '\0' != cgutil_opt.nodegroup[0]) { + fprintf(stderr, "WARNING: failed to parse the node group configure file!\n"); + } + else { + fprintf(stderr, "FATAL: failed to parse the configure file!\n"); + return -1; + } + } + } + + return 0; +} + +/* + * @Description: 检查并获取组百分比。 + * @IN percent: 输入的百分比 + * @IN gtype: 组类型:class、workload或top + * @Return: -1: 异常 0: 正常 + * @See also: + * 函数用于检查并获取组百分比。根据传入的组类型(gtype)参数的值,设置相应的百分比值。如果组类型不匹配,则返回 - 1并输出错误信息。函数参数为百分比(percent)和组类型(gtype)。 + */ +static int check_and_get_group_percent(char* percent, char* gtype) +{ + char* bad = NULL; + + if (strcmp(gtype, "top") == 0) { // 如果组类型是top,则设置top百分比 + cgutil_opt.toppct = (int)strtol(percent, &bad, 10); + } + else if (strcmp(gtype, "class") == 0) { // 如果组类型是class,则设置class百分比 + cgutil_opt.clspct = (int)strtol(percent, &bad, 10); + } + else if (strcmp(gtype, "workload") == 0) { // 如果组类型是workload,则设置workload百分比 + cgutil_opt.grppct = (int)strtol(percent, &bad, 10); + } + else if (strcmp(gtype, "backend") == 0) { // 如果组类型是backend,则设置backend百分比 + cgutil_opt.bkdpct = (int)strtol(percent, &bad, 10); + } + else { // 如果组类型不匹配,则返回异常 + return -1; + } + + if ((bad != NULL) && *bad) { + fprintf(stderr, "ERROR: incorrect %s percent %s!\n", gtype, percent); + return -1; + } + + return 0; +} +/* + * function name: parse_options + * description : 解析 gs_cgroup 工具的选项 + * arguments : 作为主函数参数的 argc 和 argv + * return value : + * -1: 异常 + * 0: 正常 + * + */ +static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {"abort", no_argument, NULL, 'a'}, + {"group", required_argument, NULL, 'N'}, + {"penalty", no_argument, NULL, 1}, + {"upgrade", no_argument, NULL, 2}, + {"refresh", no_argument, NULL, 3}, + {"revert", no_argument, NULL, 4}, + {"fixed", no_argument, NULL, 5}, + {"recover", no_argument, NULL, 6}, + {"rename", no_argument, NULL, 7}, + {NULL, 0, NULL, 0} }; + +static int parse_options(int argc, char** argv) +{ + int c; + int option_index; + int bkd = 0; + int grp = 0; + int cls = 0; + int top = 0; + int last = 0; + errno_t sret; + + sret = memset_s(&cgutil_opt, sizeof(cgutil_opt_t), 0, sizeof(cgutil_opt_t)); + securec_check_errno(sret, , -1); + + /* 选项解析 */ + while ((c = getopt_long( + argc, argv, "ab:B:cdD:E:f:hH:g:G:mMN:pPr:R:s:S:t:T:uU:Vw:W:", long_options, &option_index)) != -1) { + switch (c) { + case 'a': /* 中止 */ + if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) + cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ABORT); + else + cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ERROR); + break; + case 'b': /* 后端组百分比 */ + if (check_and_get_group_percent(optarg, "backend") == -1) + return -1; + + bkd = 1; + break; + case 'B': /* 后端组名称 */ + sret = strncpy_s(cgutil_opt.bkdname, GPNAME_LEN, optarg, GPNAME_LEN - 1); + securec_check_errno(sret, , -1); + + check_input_for_security(cgutil_opt.bkdname); + + break; + case 'c': /* 创建组 */ + cgutil_opt.cflag = 1; + break; + case 'd': /* 删除组 */ + cgutil_opt.dflag = 1; + break; + case 'D': /* 挂载点 */ + cgutil_opt.mpflag = 1; + sret = strncpy_s(cgutil_opt.mpoint, MAXPGPATH, optarg, MAXPGPATH - 1); + securec_check_errno(sret, , -1); + + check_input_for_security(cgutil_opt.mpoint); + + last = strlen(cgutil_opt.mpoint) - 1; + if ('/' == cgutil_opt.mpoint[last]) + cgutil_opt.mpoint[last] = '\0'; + break; + case 'E': /* 异常数据 */ + sret = strncpy_s(cgutil_opt.edata, EXCEPT_LEN, optarg, EXCEPT_LEN - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.edata); + } + ```cpp + // 功能:解析命令行参数 + // 参数: + // -h:显示帮助信息 + // -H:设置GAUSSHOME路径 + // -f:设置核心数 + // -g:设置工作负载组百分比 + // -G:设置工作负载组名称 + // -m:挂载cgroup + // -M:卸载cgroup + // -N:设置节点组信息 + // -p:显示gscgroup.cfg信息 + // -P:显示Cgroup树信息 + // -s:设置类组百分比 + // -S:设置类组名称 + // -t:设置Top组百分比 + // -T:设置Top组名称 + // -u:设置更新标志 + // -U:设置用户名 + // -V:显示版本信息 + // return:检查解析后的输入是否有效,并返回结果 + + int parse_args(int argc, char* argv[]) { + int opt; + int grp = 0; + int cls = 0; + int top = 0; + + while ((opt = getopt(argc, argv, "hH:f:g:G:mMN:pPsS:t:T:uUV")) != -1) { + switch (opt) { + case 'h': /* 帮助 */ + usage(); + exit(0); + case 'H': /* GAUSSHOME路径 */ + sret = strncpy_s(cgutil_opt.hpath, MAXPGPATH, optarg, MAXPGPATH - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.hpath); + break; + case 'f': /* 核心数 */ + if (check_cpuset_value_valid(optarg) == -1) + return -1; + break; + case 'g': /* 工作负载组百分比 */ + if (check_and_get_group_percent(optarg, "workload") == -1) + return -1; + + grp = 1; + break; + case 'G': /* 工作负载组名称("gpname:gplevel") */ + sret = strncpy_s(cgutil_opt.wdname, GPNAME_LEN, optarg, GPNAME_LEN - 1 - 2); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.wdname); + break; + case 'm': /* 挂载cgroup */ + cgutil_opt.mflag = 1; + break; + case 'M': /* 卸载cgroup */ + cgutil_opt.umflag = 1; + break; + case 'N': /* 节点组信息 */ + sret = strncpy_s(cgutil_opt.nodegroup, GPNAME_LEN, optarg, GPNAME_LEN - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.nodegroup); + break; + case 'p': /* 显示gscgroup.cfg信息 */ + cgutil_opt.display = 1; + break; + case 'P': /* 显示Cgroup树信息 */ + cgutil_opt.ptree = 1; + break; + case 's': /* 类组百分比 */ + if (check_and_get_group_percent(optarg, "class") == -1) + return -1; + + cls = 1; + break; + case 'S': /* 类组名称 */ + if (strchr(optarg, ':') != NULL) { + fprintf(stderr, "ERROR, class cannot be named with ':'. \n"); + return -1; + } + sret = strncpy_s(cgutil_opt.clsname, GPNAME_LEN, optarg, GPNAME_LEN - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.clsname); + break; + case 't': /* Top组百分比 */ + if (check_and_get_group_percent(optarg, "top") == -1) + return -1; + + top = 1; + break; + case 'T': /* Top组名称 */ + sret = strncpy_s(cgutil_opt.topname, GPNAME_LEN, optarg, GPNAME_LEN - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.topname); + break; + case 'u': /* 更新标志 */ + cgutil_opt.uflag = 1; + break; + case 'U': /* 用户名 */ + sret = strncpy_s(cgutil_opt.user, USERNAME_LEN, optarg, USERNAME_LEN - 1); + securec_check_errno(sret, , -1); + check_input_for_security(cgutil_opt.user); + break; + case 'V': /* 版本 */ + cgutil_version = DEF_GS_VERSION; + return 0; + case 1: + if (IS_EXCEPT_FLAG(cgutil_opt.eflag, EXCEPT_NONE)) + cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_PENALTY); + else + cgutil_opt.eflag = EXCEPT_FLAG(EXCEPT_ERROR); + break; + case 2: + cgutil_opt.upgrade = 1; + break; + case 3: + cgutil_opt.refresh = 1; + break; + case 4: + cgutil_opt.revert = 1; + break; + case 5: + cgutil_opt.fixed = 1; + break; + case 6: + cgutil_opt.recover = 1; + break; + case 7: + cgutil_opt.rename = 1; + break; + default: + fprintf(stderr, "ERROR: 错误的选项: %s\n.", optarg); + usage(); + return -1; + } + } + + return check_input_isvalid(bkd, grp, cls, top); + } + /* + * 函数名:main + * 描述:gs_cgroup实用程序的主入口 + * 参数:main函数的默认参数 + */ + int main(int argc, char** argv) + { + char* cpuset = NULL; + int ret = 0; + + if (argc < 2) { + usage(); // 使用说明 + exit(-1); + } + + // 日志输出重定向 + init_log(PROG_NAME); // 初始化日志 + + /* 打印有关gs_cgroup参数的日志 */ + char arguments[MAX_BUF_SIZE] = { 0x00 }; // 存储所有参数的字符串 + for (int i = 0; i < argc; i++) { + errno_t rc = strcat_s(arguments, MAX_BUF_SIZE, argv[i]); // 将参数拼接到arguments字符串中 + size_t len = strlen(arguments); + if (rc != EOK || len >= (MAX_BUF_SIZE - 2)) + break; + arguments[len] = ' '; + arguments[len + 1] = '\0'; + } + write_log("The gs_cgroup run with the following arguments: [%s].\n", arguments); // 输出包含参数的日志 + + /* 获取CPU核心数 */ + cgutil_cpucnt = gsutil_get_cpu_count(); + + if (cgutil_cpucnt == -1) { + fprintf(stderr, + "获取CPU核心范围失败,请检查是否可接受\"/proc/cpuinfo\"或\"/sys/devices/system\"路径。\n"); + exit(-1); + } + + int rc = sprintf_s(cgutil_allset, sizeof(cgutil_allset), "%d-%d", 0, cgutil_cpucnt - 1); // 设置allset字符串的值 + securec_check_intval(rc, , -1); + + /* 解析选项 */ + ret = parse_options(argc, argv); // 解析命令行选项 + if (-1 == ret) { + fprintf(stderr, "HINT: 请运行 'gs_cgroup -h' 显示使用方法!\n"); + exit(-1); + } + + if (cgutil_version != NULL) { + fprintf(stdout, "gs_cgroup %s\n", cgutil_version); + return 0; + } + + if (geteuid() == 0 && cgutil_opt.mflag) { + cgexec_mount_cgroups(); // 挂载cgroups + } + + if (geteuid() == 0 && cgutil_opt.umflag && !cgutil_opt.dflag) { + cgexec_umount_cgroups(); // 卸载cgroups + exit(0); + } + + /* 检索配置文件的信息;如果没有,则创建一个 */ + if (initialize_cgroup_config() == -1) // 初始化cgroup配置 + return -1; + + /* 检查升级标志 */ + if (cgutil_opt.upgrade) { + cgutil_opt.refresh = 1; + + /* 可能不需要进行升级 */ + if (cgexec_check_mount_for_upgrade() == -1) { + goto error; + } + } + + if (cgutil_opt.cflag) { + /* 以root用户身份运行 */ + if (geteuid() == 0 && cgutil_opt.upgrade == 0) { + /* 如果未指定mflag,则检查cgroups是否已挂载 */ + if (-1 == (ret = cgexec_mount_root_cgroup())) { + goto error; + } + } + } + + /* 初始化libcgroup */ + ret = cgroup_init(); // 初始化libcgroup + if (ret) { + fprintf(stderr, + "致命错误:libcgroup初始化失败:%s\n" + "请用root用户运行 'gs_cgroup -m' 挂载cgroup!\n", + cgroup_strerror(ret)); + goto error; + } + + /* 获取内存设置 */ + if (-1 == cgexec_get_cgroup_cpuset_info(TOPCG_ROOT, &cpuset)) { + fprintf(stderr, "错误:在初始化期间获取cpusets和mems失败。\n"); + goto error; + } + + rc = snprintf_s(cgutil_allset, CPUSET_LEN, CPUSET_LEN - 1, "%s", cpuset); // 设置allset字符串的值 + securec_check_intval(rc, , -1); + free(cpuset); + cpuset = NULL; + + /* 创建/删除/更新操作 */ + if (cgutil_opt.cflag) { + if (cgexec_create_groups() == -1) { + goto error; + } + } + else if (cgutil_opt.dflag) { + if (cgexec_drop_groups() == -1) { + goto error; + } + } + else if (cgutil_opt.uflag) { + if (cgexec_update_groups() == -1) { + goto error; + } + } + else if (cgutil_opt.revert) { + if (cgexec_revert_groups() == -1) { + goto error; + } + } + + /* 刷新当前组 */ + if (cgutil_opt.refresh) { + if (cgexec_refresh_groups() == -1) { + goto error; + } + } + + /* 恢复组的最后更改 */ + if (cgutil_opt.recover) { + if (cgexec_recover_groups() == -1) { + goto error; + } + } + + /* 处理异常数据 */ + if (*cgutil_opt.edata && *cgutil_opt.clsname && -1 == cgexcp_class_exception()) + goto error; + + /* 显示cgroup配置文件信息 */ + if (cgutil_opt.display) + cgconf_display_groups(); + + /* 显示cgroup树信息 */ + if (cgutil_opt.ptree) { + if (cgptree_display_cgroups() == -1) { + goto error; + } + } + + if (cgutil_vaddr[0] != NULL) + (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); + + return 0; + error: + if (cgutil_vaddr[0] != NULL) + (void)munmap(cgutil_vaddr[0], GSCGROUP_ALLNUM * sizeof(gscgroup_grp_t)); + write_log("gs_cgroup执行错误。\n"); + exit(-1); + } + /* + 该函数的功能是设置默认的cgroup组。包括设置根组、后端组、类别组和后端cgroup组的信息。 + 函数包含的变量及其功能: + - errno_t sret: 用于保存字符串拷贝操作的返回值 + - char tmpstr[GPNAME_LEN]: 临时字符串缓冲区 + + 类似的应用实例: + 该函数在初始化cgroup时使用。通过设置默认组,可以方便地管理和控制cgroup中的任务和资源。 + + 代码中各语句的功能: + - cgutil_vaddr[TOPCG_ROOT]->used = 1: 将根组的"used"字段设置为1,表示该组已被使用 + - cgutil_vaddr[TOPCG_ROOT]->gid = TOPCG_ROOT: 设置根组的gid为TOPCG_ROOT + - cgutil_vaddr[TOPCG_ROOT]->gtype = GROUP_TOP: 设置根组的gtype为GROUP_TOP + - sret = strncpy_s(cgutil_vaddr[TOPCG_ROOT]->grpname, GPNAME_LEN, GSCGROUP_ROOT, GPNAME_LEN - 1): 将GSCGROUP_ROOT字符串拷贝到根组的grpname字段中 + - cgutil_vaddr[TOPCG_ROOT]->ginfo.top.percent = 100 * DEFAULT_IO_WEIGHT / MAX_IO_WEIGHT: 计算根组的ginfo.top.percent值 + - cgutil_vaddr[TOPCG_ROOT]->ainfo.weight = DEFAULT_IO_WEIGHT: 设置根组的ainfo.weight为DEFAULT_IO_WEIGHT + - cgutil_vaddr[TOPCG_ROOT]->percent = 1000: 设置根组的percent为1000 + + - (void)sprintf_s(cgutil_vaddr[TOPCG_ROOT]->cpuset, CPUSET_LEN, "%s", cgutil_allset): 将cgutil_allset字符串拷贝到根组的cpuset字段中 + - cgutil_vaddr[TOPCG_BACKEND]->used = 1: 将后端组的"used"字段设置为1,表示该组已被使用 + - cgutil_vaddr[TOPCG_BACKEND]->gid = TOPCG_BACKEND: 设置后端组的gid为TOPCG_BACKEND + - cgutil_vaddr[TOPCG_BACKEND]->gtype = GROUP_TOP: 设置后端组的gtype为GROUP_TOP + - sret = strncpy_s(cgutil_vaddr[TOPCG_BACKEND]->grpname, GPNAME_LEN, GSCGROUP_TOP_BACKEND, GPNAME_LEN - 1): 将GSCGROUP_TOP_BACKEND字符串拷贝到后端组的grpname字段中 + - cgutil_vaddr[TOPCG_BACKEND]->ginfo.top.percent = TOP_BACKEND_PERCENT: 设置后端组的ginfo.top.percent为TOP_BACKEND_PERCENT + - cgutil_vaddr[TOPCG_BACKEND]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_BACKEND_PERCENT / 10: 计算后端组的ainfo.shares值 + - cgutil_vaddr[TOPCG_BACKEND]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_BACKEND_PERCENT): 计算后端组的ainfo.weight值 + - cgutil_vaddr[TOPCG_BACKEND]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_BACKEND_PERCENT / 100: 计算后端组的percent值 + + - if (*cgutil_vaddr[TOPCG_BACKEND]->cpuset == '\0'): 判断后端组的cpuset字段是否为空 + - (void)sprintf_s(cgutil_vaddr[TOPCG_BACKEND]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset): 将cgutil_vaddr[TOPCG_GAUSSDB]->cpuset字符串拷贝到后端组的cpuset字段中 + + - cgutil_vaddr[TOPCG_CLASS]->used = 1: 将类别组的"used"字段设置为1,表示该组已被使用 + - cgutil_vaddr[TOPCG_CLASS]->gid = TOPCG_CLASS: 设置类别组的gid为TOPCG_CLASS + - cgutil_vaddr[TOPCG_CLASS]->gtype = GROUP_TOP: 设置类别组的gtype为GROUP_TOP + - sret = strncpy_s(cgutil_vaddr[TOPCG_CLASS]->grpname, GPNAME_LEN, GSCGROUP_TOP_CLASS, GPNAME_LEN - 1): 将GSCGROUP_TOP_CLASS字符串拷贝到类别组的grpname字段中 + - cgutil_vaddr[TOPCG_CLASS]->ginfo.top.percent = TOP_CLASS_PERCENT: 设置类别组的ginfo.top.percent为TOP_CLASS_PERCENT + - cgutil_vaddr[TOPCG_CLASS]->ainfo.shares = DEFAULT_CPU_SHARES * TOP_CLASS_PERCENT / 10: 计算类别组的ainfo.shares值 + - cgutil_vaddr[TOPCG_CLASS]->ainfo.weight = IO_WEIGHT_CALC(MAX_IO_WEIGHT, TOP_CLASS_PERCENT): 计算类别组的ainfo.weight值 + - cgutil_vaddr[TOPCG_CLASS]->percent = cgutil_vaddr[TOPCG_GAUSSDB]->percent * TOP_CLASS_PERCENT / 100: 计算类别组的percent值 + + - if (*cgutil_vaddr[TOPCG_CLASS]->cpuset == '\0'): 判断类别组的cpuset字段是否为空 + - (void)sprintf_s(cgutil_vaddr[TOPCG_CLASS]->cpuset, CPUSET_LEN, "%s", cgutil_vaddr[TOPCG_GAUSSDB]->cpuset): 将cgutil_vaddr[TOPCG_GAUSSDB]->cpuset字符串拷贝到类别组的cpuset字段中 + + - cgutil_vaddr[BACKENDCG_START_ID]->used = 1: 将后端cgroup组的"used"字段设置为1,表示该组已被使用 + - cgutil_vaddr[BACKENDCG_START_ID]->gid = BACKENDCG_START_ID: 设置后端cgroup组的gid为BACKENDCG_START_ID + - cgutil_vaddr[BACKENDCG_START_ID]->gtype = GROUP_BAKWD: 设置后端cgroup组的gtype为GROUP_BAKWD + - cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.tgid = TOPCG_BACKEND: 设置后端cgroup组的ginfo.cls.tgid为TOPCG_BACKEND + - cgutil_vaddr[BACKENDCG_START_ID]->ginfo.cls.percent = DEFAULT_BACKEND_PERCENT: 设置后端cgroup组的ginfo.cls.percent为DEFAULT_BACKEND_PERCENT + - sret = strncpy_s(cgutil_vaddr[BACKENDCG_START_ID]->grpname, GPNAME_LEN, GSCGROUP_DEFAULT_BAC: 将GSCGROUP_DEFAULT_BAC字符串拷贝到后端cgroup组的grpname字段中 + */ + /** + * 生成默认配置文件的函数 + * 参数: + * vaddr - 指向gscgroup_grp_t类型的指针,表示cgroup的组内存指针 + * 功能: + * 生成默认配置文件,将配置文件存储在vaddr指向的内存中 + * 应用实例: + * cgconf_generate_default_config_file(vaddr); + */ + + /** + * 更新剩余cgroup的cpuset的函数 + * 参数: + * cls - 表示cgroup的类别 + * cpuset - 表示cpuset的字符串 + * update - 表示是否要更新cpuset的标志位 + * 返回值: + * 成功返回0,失败返回-1 + * 功能: + * 更新指定类别的cgroup的cpuset + * 应用实例: + * cgexec_update_remain_cgroup_cpuset(1, "0-3", 1); + */ + + /** + * 检查cpuset值的函数 + * 参数: + * clsset - 表示类别的cpuset字符串 + * grpset - 表示组的cpuset字符串 + * 返回值: + * 成功返回0,失败返回-1 + * 功能: + * 检查指定类别和组的cpuset值是否符合要求 + * 应用实例: + * cgexec_check_cpuset_value("0-3", "0-7"); + */ + + /** + * 更新指定类别的cpuset值的函数 + * 参数: + * cls - 表示类别 + * cpuset - 表示cpuset的字符串 + * 返回值: + * 成功返回0,失败返回-1 + * 功能: + * 更新指定类别的cpuset值 + * 应用实例: + * cgexec_update_class_cpuset(1, "0-3"); + */ + + /** + * 更新顶层组的cpuset值的函数 + * 参数: + * top - 表示顶层组 + * cpuset - 表示cpuset的字符串 + * 返回值: + * 成功返回0,失败返回-1 + * 功能: + * 更新指定顶层组的cpuset值 + * 应用实例: + * cgexec_update_top_group_cpuset(1, "0-7"); + */ + + /** + * 更新固定配置的函数 + * 参数: + * high - 表示高优先级标志位 + * extended - 表示扩展标志位 + * 功能: + * 更新固定配置的相关变量的值 + * 该函数没有返回值 + * 应用实例: + * cgexec_update_fixed_config(1, 0); + */ + + /** + * cgroup单元测试用例的函数 + * 功能: + * 执行cgroup的单元测试用例 + * 该函数没有返回值 + */ + void cgroup_unit_test_case() + { + char* argv[] = { "gs_cgroup", "-D", "/dev/cgroups/test", "--upgrade" }; + int argc = sizeof(argv) / sizeof(*argv); + int sret = 0; + + cgutil_opt.mpflag = 1; + (void)cgexec_mount_root_cgroup(); // 挂载根cgroup + (void)cgexec_umount_root_cgroup(); // 卸载根cgroup + + (void)parse_options(argc, argv); // 解析选项 + + for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { + if (NULL == (cgutil_vaddr[i] = (gscgroup_grp_t*)malloc(sizeof(gscgroup_grp_t)))) { // 分配内存 + fprintf(stderr, "ERROR: failed to allocate memory for gsgroup!\n"); + for (int index = 0; index < i; ++index) { + free(cgutil_vaddr[index]); // 释放内存 + cgutil_vaddr[index] = NULL; + } + return; + } + } + + cgroup_set_default_group(); // 设置默认组 + + for (int i = 0; i < GSCGROUP_ALLNUM; ++i) { + free(cgutil_vaddr[i]); // 释放内存 + cgutil_vaddr[i] = NULL; + } + gscgroup_grp_t vaddr[GSCGROUP_ALLNUM]; // 创建gscgroup_grp_t类型的数组 + + cgconf_generate_default_config_file(vaddr); // 生成默认配置文件 + + sret = memset_s(&cgutil_opt, sizeof(cgutil_opt), 0, sizeof(cgutil_opt)); // 清空cgutil_opt + securec_check_c(sret, "\0", "\0"); + + sret = snprintf_s(cgutil_opt.sets, sizeof(cgutil_opt.sets), sizeof(cgutil_opt.sets) - 1, "%s", "2-8"); // 设置cgutil_opt.sets + securec_check_ss_c(sret, "\0", "\0"); + + cgconf_set_class_group(1); // 设置类组 + + sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "wg1"); // 设置cgutil_opt.wdname + securec_check_ss_c(sret, "\0", "\0"); + + cgconf_set_class_group(1); // 设置类组 + + cgconf_set_workload_group(1, 2); // 设置工作负载组 + + cgutil_opt.fixed = 1; + cgutil_is_sles11_sp2 = 0; + check_percentage_value(1, 1, 1, 1); // 检查百分比值 + + cgutil_opt.display = 1; + cgutil_opt.user[0] = '\0'; + check_user_process(); // 检查用户进程 + + cgutil_opt.display = 0; + cgutil_opt.ptree = 1; + check_user_process(); // 检查用户进程 + + cgutil_opt.ptree = 0; + cgutil_opt.cflag = 1; + cgutil_opt.fixed = 0; + + sret = snprintf_s(cgutil_opt.topname, sizeof(cgutil_opt.topname), sizeof(cgutil_opt.topname) - 1, "%s", "Gaussdb"); // 设置cgutil_opt.topname + securec_check_ss_c(sret, "\0", "\0"); + + check_flag_process(); // 检查标志位进程 + + cgutil_opt.topname[0] = '\0'; + sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "wg1"); // 设置cgutil_opt.wdname + securec_check_ss_c(sret, "\0", "\0"); + + check_flag_process(); // 检查标志位进程 + + sret = snprintf_s(cgutil_opt.wdname, sizeof(cgutil_opt.wdname), sizeof(cgutil_opt.wdname) - 1, "%s", "class1:wg1"); // 设置cgutil_opt.wdname + securec_check_ss_c(sret, "\0", "\0"); + + check_flag_process(); // 检查标志位进程 + + cgutil_opt.cf: + } \ No newline at end of file -- 2.34.1 From 3b20740c1ce409858e5e0f3ede368ae9b1538072 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:03:57 +0800 Subject: [PATCH 11/56] Delete 'src/bin/gs_guc/cluster_config.cpp' --- src/bin/gs_guc/cluster_config.cpp | 1099 ----------------------------- 1 file changed, 1099 deletions(-) delete mode 100644 src/bin/gs_guc/cluster_config.cpp diff --git a/src/bin/gs_guc/cluster_config.cpp b/src/bin/gs_guc/cluster_config.cpp deleted file mode 100644 index 77b83c08e..000000000 --- a/src/bin/gs_guc/cluster_config.cpp +++ /dev/null @@ -1,1099 +0,0 @@ -/* - * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group - * - * 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. - *--------------------------------------------------------------------------------------- - * - * cluster_config.cpp - * Interfaces for analysis manager of PDK tool. - * - * Function List: find_gucoption_available - * freefile - * getnodename - * get_local_cordinator_dbpath - * get_local_datanode_dbpath - * get_local_dbpath_by_instancename - * get_local_gtmproxy_dbpath - * get_local_gtm_dbpath - * get_local_gtm_name - * get_local_gtm_proxy_name - * get_local_instancename_by_dbpath - * get_local_num_datanode - * get_nodeidx_by_name - * get_node_nodename - * get_num_nodes - * get_value_in_config_file - * init_gauss_cluster_config - * is_local_node - * is_local_nodeid - * readfile - * - * IDENTIFICATION - * src/bin/gs_guc/cluster_config.cpp - * - * --------------------------------------------------------------------------------------- - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/config/cm_config.h" -#include "bin/elog.h" -#include "securec.h" -#include "securec_check.h" -#include "port.h" - -#define MAX_VALUE_LEN 1024 -#define MAX_PARAM_LEN 1024 -#define CLUSTER_CONFIG_SUCCESS 0 -#define CLUSTER_CONFIG_ERROR 1 -#define CM_NODE_NAME_LEN 64 - -#define STADARD_SSH_PORT 22 - -#define STD_FORMAT_ARG_POSITION 2 - -extern char** cndn_param; -extern char** cmserver_param; -extern char** cmagent_param; -extern char** gtm_param; -extern char** lc_param; -extern char** cndn_guc_info; -extern char** cmserver_guc_info; -extern char** cmagent_guc_info; -extern char** gtm_guc_info; -extern char** lc_guc_info; -extern int cndn_param_number; -extern int cmserver_param_number; -extern int cmagent_param_number; -extern int gtm_param_number; -extern int lc_param_number; -extern uint32 g_local_dn_idx; -extern char* g_current_data_dir; - -const int g_min_ip_len = 7; // IPV4 and IPV6, choose the minimum length - -#ifndef GS_COLLECTOR_BUILD -extern void write_stderr(const char* fmt, ...) - /* This extension allows gcc to check the format string for consistency with - the supplied arguments. */ - __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, STD_FORMAT_ARG_POSITION))); -#else -#define write_stderr printf -#endif - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -typedef enum { - INSTANCE_ANY, - INSTANCE_DATANODE, /* postgresql.conf */ - INSTANCE_COORDINATOR, /* postgresql.conf */ - INSTANCE_GTM, /* gtm.conf */ - INSTANCE_GTM_PROXY, /* gtm_proxy.conf */ - INSTANCE_CMAGENT, /* cm_agent.conf */ - INSTANCE_CMSERVER, /* cm_server.conf */ - INSTANCE_DATAINSTANCE, /* postgresql.conf */ -} NodeType; - -/* Define all the node types */ -typedef enum { - GUC_NONE = 0, - GUC_CNDN, - GUC_GTM, - GUC_CMSERVER, - GUC_CMAGENT, - GUC_LCNAME -} GUC_Node_Type; - -const int INVALID_LINES_IDX = -1; -#define GUC_OPT_CONF_FILE "cluster_guc.conf" -#define SUCCESS 0 -#define FAILURE 1 -#define GS_FREE(ptr) \ - do { \ - if (NULL != (ptr)) { \ - free((char*)(ptr)); \ - ptr = NULL; \ - } \ - } while (0) - -int32 get_local_instancename_by_dbpath(const char* dbpath, char* instancename); -int32 get_local_gtm_name(char* instancename); -int get_value_in_config_file(const char* pg_config_file, const char* parameter_in_config, char* para_value); -int get_all_datanode_num(); -int get_all_coordinator_num(); -int get_all_cmserver_num(); -int get_all_cmagent_num(); -int get_all_cndn_num(); -int get_all_gtm_num(); -char* get_AZname_by_nodename(const char* nodename); -int find_gucoption_available( - const char** optlines, const char* opt_name, int* name_offset, int* name_len, int* value_offset, int* value_len); -char** readfile(const char* path, int reserve_num_lines); - -void freefile(char** lines); - -extern bool get_env_value(const char* env_var, char* output_env_value, size_t env_var_value_len); -void* pg_malloc_memory(size_t size); -extern char* xstrdup(const char* s); -extern char* g_local_instance_path; -extern void check_env_value(const char* input_env_value); - -/* - ****************************************************************************** - Function : get_local_num_datanode - Description : - Input : - Output : None - Return : None - ****************************************************************************** -*/ -uint32 get_local_num_datanode() -{ - return g_currentNode->datanodeCount; -} - -/* - ****************************************************************************** - Function : get_num_nodes - Description : - Input : - Output : None - Return : None - ****************************************************************************** -*/ -uint32 get_num_nodes() -{ - return g_node_num; -} - -/* - ****************************************************************************** - Function : is_local_nodeid - Description : check the input node id is current node id - Input : nodeid - node id - Output : None - Return : None - ****************************************************************************** -*/ -bool is_local_nodeid(uint32 nodeid) -{ - return (g_currentNode->node == nodeid); -} - -#ifdef GS_COLLECTOR_BUILD -staticNodeConfig* get_node_nodename(char* name); - -/* - ****************************************************************************** - Function : get_node_nodename - Description : - Input : nodename - - Output : None - Return : None - ****************************************************************************** -*/ -staticNodeConfig* get_node_nodename(char* nodename) -{ - uint32 nodeidx = 0; - for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { - if (0 == strncmp(g_node[nodeidx].nodeName, nodename, CM_NODE_NAME_LEN)) { - return &g_node[nodeidx]; - } - } - - return NULL; -} - -#endif - -/* - ****************************************************************************** - Function : get_nodeidx_by_name - Description : - Input : nodename - node name - Output : None - Return : uint32 - node id index - ****************************************************************************** -*/ -int32 get_nodeidx_by_name(const char* nodename) -{ - uint32 nodeidx = 0; - for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { - if (0 == strncmp(g_node[nodeidx].nodeName, nodename, CM_NODE_NAME_LEN)) { - return (int32)nodeidx; - } - } - - return -1; -} -/* - ****************************************************************************** - Function : get_all_datanode_num - Description : get all datanode instance number - ****************************************************************************** -*/ -int get_all_datanode_num() -{ - uint32 nodeidx = 0; - int count = 0; - for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { - count += (int)g_node[nodeidx].datanodeCount; - } - return count; -} -/* - ****************************************************************************** - Function : get_all_coordinator_num - Description : get all coordinator instance number - ****************************************************************************** -*/ -int get_all_coordinator_num() -{ - uint32 nodeidx = 0; - int count = 0; - for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { - if (1 == g_node[nodeidx].coordinate) { - count += 1; - } - } - return count; -} -/* - ****************************************************************************** - Function : get_all_cmserver_num - Description : get all cm_server instance number - ****************************************************************************** -*/ -int get_all_cmserver_num() -{ - uint32 nodeidx; - int count = 0; - for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { - if (1 == g_node[nodeidx].cmServerLevel && g_node[nodeidx].cmDataPath[0] != '\0') { - count += 1; - } - } - - return count; -} -/* - ****************************************************************************** - Function : get_all_cmagent_num - Description : get all cm_agent instance number - ****************************************************************************** -*/ -int get_all_cmagent_num() -{ - return get_num_nodes(); -} - -/* - ****************************************************************************** - Function : get_all_cndn_num - Description : get all CN and DN instance number - ****************************************************************************** -*/ -int get_all_cndn_num() -{ - int count = 0; - count = get_all_datanode_num() + get_all_coordinator_num(); - - return count; -} - -int get_all_gtm_num() -{ - uint32 nodeidx = 0; - int count = 0; - for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { - if (1 == g_node[nodeidx].gtm && g_node[nodeidx].gtmLocalDataPath[0] != '\0') { - count += 1; - } - } - return count; -} - -/* - ****************************************************************************** - Function : is_local_node - Description : - Input : nodename - - Output : None - Return : None - ****************************************************************************** -*/ -bool is_local_node(const char* nodename) -{ - return (0 == strncmp(g_currentNode->nodeName, nodename, CM_NODE_NAME_LEN)); -} - -/* - ****************************************************************************** - Function : getnodename - Description : get node name by node id index - Input : nodeidx - node id index - Output : None - Return : char * - node name - ****************************************************************************** -*/ -char* getnodename(uint32 nodeidx) -{ - return g_node[nodeidx].nodeName; -} - -/* - ****************************************************************************** - Function : get_hostname_or_ip - Description : if In agent mode, there is an environment variable HOST_IP writen in /etc/profile, - then get host ip from environment variables. - Else, get hostname for adaptation to the previous version. - Input : name_len - the length of ip or hostname - Output : out_name - the host ip get from environment variables or the hostname - Return : bool - ****************************************************************************** -*/ -bool get_hostname_or_ip(char* out_name, size_t name_len) -{ - int rc = 0; - char* env_value = NULL; - - if (out_name == NULL) { - (void)write_stderr("ERROR: Get NULL point from upper function when get hostip or hostname.\n"); - return false; - } - - env_value = gs_getenv_r("HOST_IP"); - if (env_value != NULL) { - check_env_value(env_value); - } - - if ((env_value == NULL) || (env_value[0] == '\0')) { - (void)gethostname(out_name, name_len); - if (out_name[0] == '\0') { - return false; - } - } else { - if (strlen(env_value) >= name_len) { - (void)write_stderr("ERROR: The value of environment variable HOST_IP is too long.\n"); - return false; - } - - if (strlen(env_value) < g_min_ip_len) { - (void)write_stderr("ERROR: The value of environment variable HOST_IP is too short.\n"); - return false; - } - - rc = strcpy_s(out_name, name_len, env_value); - securec_check_c(rc, "\0", "\0"); - } - return true; -} - -/* - ****************************************************************************** - Function : get_backIps_by_nodename - Description : get back ip by node name, - Input : nodename - - Output : ipAddress - Return : ipAddress - ****************************************************************************** -*/ -char* get_backIps_by_nodename(const char* nodename) -{ - uint32 nodeidx = 0; - char* ipAddress = NULL; - for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { - if (strcmp(g_node[nodeidx].nodeName, nodename) == 0) { - // Assignment by cluster_static_config value - ipAddress = xstrdup(g_node[nodeidx].backIps[0]); - } - } - return ipAddress; -} - -/* - ****************************************************************************** - Function : is_instance_in_nodename - Description : check is the instance in node - Input : nodename - - Output : bool - ****************************************************************************** -*/ -bool is_instance_in_nodename(const char* nodename) -{ - uint32 i; - char* backIp = NULL; - - backIp = get_backIps_by_nodename(nodename); - if (NULL == backIp) { - return false; - } - - for (i = 0; i < g_currentNode->datanodeCount; i++) { - if (strcmp(g_currentNode->datanode[i].datanodeLocalDataPath, g_local_instance_path) == 0) { - for (uint32 dnId = 0; dnId < CM_MAX_DATANODE_STANDBY_NUM; dnId++) { - if (strcmp(backIp, g_currentNode->datanode[i].peerDatanodes[dnId].datanodePeerHAIP[0]) == 0) { - GS_FREE(backIp); - return true; - } - } - } - } - GS_FREE(backIp); - return false; -} -/* - ****************************************************************************** - Function : get_AZname_by_nodename - Description : get az name list by node name, - Input : nodename - - Output : azname - Return : azname - ****************************************************************************** -*/ -char* get_AZname_by_nodename(const char* nodename) -{ - uint32 nodeidx; - char* azName = NULL; - for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { - if (strcmp(g_node[nodeidx].nodeName, nodename) == 0) { - // Assignment by cluster_static_config value - azName = xstrdup(g_node[nodeidx].azName); - } - } - return azName; -} - -/* - ****************************************************************************** - Function : get_local_dbpath_by_instancename - Description : get the instance directory where the instance is located by instance name - Input : instancename - the instance name - type - The value of the -Z parameter - dbpath - Instance of the path - Output : None - Return : None - ****************************************************************************** -*/ -int32 get_local_dbpath_by_instancename(const char* instancename, const int* type, char* dbpath) -{ - uint32 i; - char local_inst_name[CM_NODE_NAME_LEN] = {0}; - int32 retval; - errno_t rc = 0; - - if ((*type == INSTANCE_ANY) || (*type == INSTANCE_COORDINATOR)) { - if ('\0' != g_currentNode->DataPath[0]) { - retval = get_local_instancename_by_dbpath(g_currentNode->DataPath, local_inst_name); - if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { - rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->DataPath, CM_PATH_LENGTH); - securec_check_c(rc, "\0", "\0"); - return CLUSTER_CONFIG_SUCCESS; - } - } - } - - if ((*type == INSTANCE_ANY) || (*type == INSTANCE_DATANODE)) { - for (i = 0; i < g_currentNode->datanodeCount; i++) { - retval = - get_local_instancename_by_dbpath(g_currentNode->datanode[i].datanodeLocalDataPath, local_inst_name); - if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { - rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->datanode[i].datanodeLocalDataPath, CM_PATH_LENGTH); - securec_check_c(rc, "\0", "\0"); - return CLUSTER_CONFIG_SUCCESS; - } - } - } - - if ((*type == INSTANCE_ANY) || (*type == INSTANCE_GTM)) { - retval = get_local_gtm_name(local_inst_name); - if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { - rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->gtmLocalDataPath, CM_PATH_LENGTH); - securec_check_c(rc, "\0", "\0"); - return CLUSTER_CONFIG_SUCCESS; - } - } - - return CLUSTER_CONFIG_ERROR; -} - -/* - ****************************************************************************** - Function : get_local_instancename_by_dbpath - Description : - Input : dbpath - the data path of instance - instancename - the instance name, such as: dn_6002_6003 - Output : None - Return : None - ****************************************************************************** -*/ -int32 get_local_instancename_by_dbpath(const char* dbpath, char* instancename) -{ - char name[MAX_VALUE_LEN] = ""; - int retval; - char pg_config_file[MAXPGPATH] = {0}; - int nRet; - errno_t rc; - - nRet = snprintf_s(pg_config_file, MAXPGPATH, MAXPGPATH - 1, "%s/postgresql.conf", dbpath); - securec_check_ss_c(nRet, "\0", "\0"); - - retval = get_value_in_config_file(pg_config_file, "pgxc_node_name", name); - if (0 == retval) { - rc = strncpy_s(instancename, CM_NODE_NAME_LEN, name, CM_NODE_NAME_LEN - 1); - securec_check_c(rc, "\0", "\0"); - instancename[CM_NODE_NAME_LEN - 1] = '\0'; - return CLUSTER_CONFIG_SUCCESS; - } - - instancename[0] = '\0'; - - return CLUSTER_CONFIG_ERROR; -} - -/* - ****************************************************************************** - Function : get_local_gtm_name - Description : get the name of gtm from the configuration file---gtm.conf. - Find the parameter "nodename" in the file "gtm.conf" to get its corresponding parameter value - name---"one" - Input : instancename -the instance name - Output : None - Return : None - ****************************************************************************** -*/ -int32 get_local_gtm_name(char* instancename) -{ - char name[MAX_VALUE_LEN] = ""; - int retval; - char pg_config_file[MAXPGPATH] = {0}; - int nRet; - errno_t rc; - - if (g_currentNode->gtmId == 0) { - return CLUSTER_CONFIG_ERROR; - } - - nRet = snprintf_s(pg_config_file, MAXPGPATH, MAXPGPATH - 1, "%s/gtm.conf", g_currentNode->gtmLocalDataPath); - securec_check_ss_c(nRet, "\0", "\0"); - - /* Retrieves the parameter values for the specified parameters from the configuration file */ - retval = get_value_in_config_file(pg_config_file, "nodename", name); - if (0 == retval) { - rc = strncpy_s(instancename, CM_NODE_NAME_LEN, name, CM_NODE_NAME_LEN - 1); - securec_check_c(rc, "\0", "\0"); - return CLUSTER_CONFIG_SUCCESS; - } - - return CLUSTER_CONFIG_ERROR; -} - -/* - ****************************************************************************** - Function : init_gauss_cluster_config - Description : Obtain cluster information from cluster_static_config - Input : None - Output : void - Return : None - ****************************************************************************** -*/ -int init_gauss_cluster_config(void) -{ - char path[MAXPGPATH] = {0}; - char gausshome[MAXPGPATH] = {0}; - int err_no = 0; - int nRet = 0; - int status = 0; - uint32 nodeidx = 0; - struct stat statbuf {}; - - static bool is_init = false; - if (is_init) { - return 0; - } - is_init = true; - g_dn_replication_num = 0; - - if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) - return 1; - - check_env_value(gausshome); - if (NULL != g_lcname) { - nRet = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s.%s", gausshome, g_lcname, STATIC_CONFIG_FILE); - } else { - nRet = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, STATIC_CONFIG_FILE); - } - securec_check_ss_c(nRet, "\0", "\0"); - - if (checkPath(path) != 0) { - write_stderr(_("realpath(%s) failed : %s!\n"), path, strerror(errno)); - return 1; - } - - if (lstat(path, &statbuf) != 0) { - write_stderr("ERROR: could not stat file \"%s\": %s\n", path, strerror(errno)); - return 1; - } - - if (NULL != g_lcname) { - status = read_lc_config_file(path, &err_no); - } else { - status = read_config_file(path, &err_no); - } - if (0 != status) { - switch (status) { - case OPEN_FILE_ERROR: { - write_stderr("ERROR: The cluster_staic_config file is not generated or is manually deleted.\n"); - return 1; - } - case READ_FILE_ERROR: { - write_stderr("ERROR: The cluster_staic_config file permission is insufficient.\n"); - return 1; - } - case OUT_OF_MEMORY: { - write_stderr("ERROR: The cluster_staic_config open failed cause out of memeory.\n"); - return 1; - } - default: - break; - } - write_stderr("ERROR: Invalid return value from read_config_file\n"); - return 1; - } - - if (g_nodeHeader.node <= 0) { - write_stderr("ERROR: Invalid cluster_staic_config file," - " curerent node id is:%d .\n", - (int32)g_nodeHeader.node); - GS_FREE(g_node); - return 1; - } - - for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { - if (g_node[nodeidx].node == g_nodeHeader.node) { - g_currentNode = &g_node[nodeidx]; - } - } - - if (NULL == g_currentNode) { - write_stderr("ERROR: failed to find current node by nodeid, curerent node id is:%d .\n", (int32)g_nodeHeader.node); - GS_FREE(g_node); - return 1; - } - - if (get_dynamic_dn_role() != 0) { - write_stderr("ERROR: failed to get dynamic dn role.\n"); - GS_FREE(g_node); - return 1; - } - - return 0; -} - -/* - * @@GaussDB@@ - * Brief : save_guc_para_info() - * Description : get parameter of CN/DN/CMSERVER/CMAGENT from cluster_guc.conf file - * Notes : if it cann't open file, return NULL - * Input : the path of cluster_guc.conf file - * Output : the config parameter list of CN/DN/CMSERVER/CMAGENT - */ -int save_guc_para_info() -{ - int rc = 0; - errno_t ret; - FILE* fp = NULL; - char line_info[MAXPGPATH] = {0}; - char temp_line_info[MAXPGPATH] = {0}; - char* get_result = NULL; - char* outer_ptr = NULL; - GUC_Node_Type type = GUC_NONE; - char gausshome[MAXPGPATH] = {0}; - char guc_file[MAXPGPATH] = {0}; - - rc = memset_s(line_info, MAXPGPATH, 0, MAXPGPATH); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(temp_line_info, MAXPGPATH, 0, MAXPGPATH); - securec_check_c(rc, "\0", "\0"); - if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) - return FAILURE; - - check_env_value(gausshome); - rc = snprintf_s(guc_file, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, GUC_OPT_CONF_FILE); - securec_check_ss_c(rc, "\0", "\0"); - - if (checkPath(guc_file) != 0) { - write_stderr(_("realpath(%s) failed : %s!\n"), guc_file, strerror(errno)); - return FAILURE; - } - /* maybe fail because of privilege */ - fp = fopen(guc_file, "r"); - if (fp == NULL) { - write_stderr("ERROR: Failed to open file\"%s\"\n", guc_file); - return FAILURE; - } - if (NULL == fgets(line_info, MAXPGPATH - 1, fp)) { - write_stderr("ERROR: Failed to read file\"%s\"\n", guc_file); - fclose(fp); - return FAILURE; - } - - while ((fgets(line_info, MAXPGPATH - 1, fp)) != NULL) { - if ((int)strlen(line_info) > 0) - line_info[(int)strlen(line_info) - 1] = '\0'; - else - continue; - - if (line_info[0] == '#') { - continue; - } else if (strncmp(line_info, "[coordinator/datanode]", sizeof("[coordinator/datanode]")) == 0) { - type = GUC_CNDN; - continue; - } else if (strncmp(line_info, "[gtm]", sizeof("[gtm]")) == 0) { - type = GUC_GTM; - continue; - } else if (strncmp(line_info, "[cmserver]", sizeof("[cmserver]")) == 0) { - type = GUC_CMSERVER; - continue; - } else if (strncmp(line_info, "[cmagent]", sizeof("[cmagent]")) == 0) { - type = GUC_CMAGENT; - continue; - } else if (strncmp(line_info, "[lcname]", sizeof("[lcname]")) == 0) { - type = GUC_LCNAME; - continue; - } else if (strncmp(line_info, "[end]", sizeof("[end]")) == 0) { - break; - } - - ret = strcpy_s(temp_line_info, sizeof(temp_line_info), line_info); - securec_check_c(ret, "\0", "\0"); - get_result = strtok_r(line_info, "|", &outer_ptr); - if (NULL == get_result) { - write_stderr("ERROR: Line information is incorrect\n"); - fclose(fp); - return FAILURE; - } - - switch (type) { - case GUC_CNDN: - cndn_param[cndn_param_number] = xstrdup(get_result); - cndn_guc_info[cndn_param_number] = xstrdup(temp_line_info); - cndn_param_number++; - break; - case GUC_GTM: - gtm_param[gtm_param_number] = xstrdup(get_result); - gtm_guc_info[gtm_param_number] = xstrdup(temp_line_info); - gtm_param_number++; - break; - case GUC_CMSERVER: - cmserver_param[cmserver_param_number] = xstrdup(get_result); - cmserver_guc_info[cmserver_param_number] = xstrdup(temp_line_info); - cmserver_param_number++; - break; - case GUC_CMAGENT: - cmagent_param[cmagent_param_number] = xstrdup(get_result); - cmagent_guc_info[cmagent_param_number] = xstrdup(temp_line_info); - cmagent_param_number++; - break; - case GUC_LCNAME: - lc_param[lc_param_number] = xstrdup(get_result); - lc_guc_info[lc_param_number] = xstrdup(temp_line_info); - lc_param_number++; - break; - default: - fclose(fp); - return FAILURE; - } - } - - fclose(fp); - return SUCCESS; -} - -/* - * @@GaussDB@@ - * Brief : readfile(const char* path, int reserve_num_lines) - * Description : get value from directory - * Notes : if it cann't open file, return NULL - */ -char** readfile(const char* path, int reserve_num_lines) -{ - int fd; - int nlines = 0; - 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); - return NULL; - } - if (statbuf.st_size == 0) { - /* empty file */ - close(fd); - result = (char**)malloc((1 + reserve_num_lines) * sizeof(char*)); - if (NULL == result) { - write_stderr("ERROR: Memory allocation failed.\n"); - return NULL; - } - - for (i = 0; i < reserve_num_lines + 1; i++) { - result[i] = NULL; - } - - *result = NULL; - return result; - } - - if (statbuf.st_size > LONG_MAX - 1) { - write_stderr("malloc size too big, size (%ld).\n", statbuf.st_size); - close(fd); - return NULL; - } - - buffer = (char*)malloc((size_t)(statbuf.st_size + 1)); - if (NULL == buffer) { - close(fd); - write_stderr("ERROR: Memory allocation failed.\n"); - return NULL; - } - - len = read(fd, buffer, statbuf.st_size + 1); - close(fd); - if (len != statbuf.st_size) { - /* oops, the file size changed between fstat and read */ - write_stderr("ERROR: File is buzy read failed.\n"); - GS_FREE(buffer); - 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**)malloc((nlines + 1 + reserve_num_lines) * sizeof(char*)); - if (NULL == result) { - GS_FREE(buffer); - write_stderr("ERROR: Memory allocation failed.\n"); - return NULL; - } - - /* 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*)malloc(slen + 1); - if (NULL == linebuf) { - write_stderr("ERROR: Memory allocation failed.\n"); - for (i = 0; i < n; i++) { - GS_FREE(result[i]); - } - GS_FREE(result); - GS_FREE(buffer); - return NULL; - } - rc = memcpy_s(linebuf, slen, linebegin, slen); - securec_check_c(rc, "\0", "\0"); - linebuf[slen] = '\0'; - result[n++] = linebuf; - linebegin = &buffer[i + 1]; - } - } - result[n] = NULL; - - for (i = 0; i < reserve_num_lines; i++) { - result[n + i] = NULL; - } - - GS_FREE(buffer); - - return result; -} - -/******************************************************************************* - Function : get_value_in_config_file - Description : - Input : pg_config_file - configuration file, such as: postgresql.conf/gtm.conf - parameter_in_config - The name of the parameter in the configuration file, such as: dn_6002_6003 - para_value - the value of the parameter in the configuration file - Output : None - Return : None -*******************************************************************************/ -int get_value_in_config_file(const char* pg_config_file, const char* parameter_in_config, char* para_value) -{ - int values_offset = 0; - int values_len = 0; - int values_line = 0; - char** all_lines = NULL; - int rc = 0; - - all_lines = readfile(pg_config_file, 0); - if (NULL == all_lines) { - return 1; - } - values_line = - find_gucoption_available((const char**)all_lines, parameter_in_config, NULL, NULL, &values_offset, &values_len); - - if (values_line != INVALID_LINES_IDX) { - rc = strncpy_s(para_value, - MAX_VALUE_LEN, - all_lines[values_line] + values_offset + 1, - (size_t)Min(values_len - 2, MAX_VALUE_LEN - 1)); - securec_check_c(rc, "\0", "\0"); - } - - freefile(all_lines); - - return (values_line == INVALID_LINES_IDX); -} - -/******************************************************************************* - Function : find_gucoption_available - Description : - Input : optlines - - opt_name - - name_offset - - name_len - - value_offset - - value_len - - Output : None - Return : None -*******************************************************************************/ -int find_gucoption_available( - const char** optlines, const char* opt_name, int* name_offset, int* name_len, int* value_offset, int* value_len) -{ - char* p = NULL; - char* q = NULL; - char* tmp = NULL; - int i = 0; - size_t paramlen = 0; - - if (NULL == optlines || NULL == opt_name) { - 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 = (char*)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 = (NULL == tmp) ? 0 : (tmp - p); - } - return i; - } - - return INVALID_LINES_IDX; -} - -/******************************************************************************* - Function : freefile - Description : - Input : lines - - Output : None - Return : None -*******************************************************************************/ -void freefile(char** lines) -{ - char** line = NULL; - if (NULL == lines) { - return; - } - line = lines; - while (*line != NULL) { - free(*line); - *line = NULL; - line++; - } - free(lines); - lines = NULL; -} - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -- 2.34.1 From cf99a0753be36b62e18561bcd5fb048591689475 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:04:16 +0800 Subject: [PATCH 12/56] ADD file via upload --- src/bin/gs_guc/cluster_config.cpp | 1161 +++++++++++++++++++++++++++++ 1 file changed, 1161 insertions(+) create mode 100644 src/bin/gs_guc/cluster_config.cpp diff --git a/src/bin/gs_guc/cluster_config.cpp b/src/bin/gs_guc/cluster_config.cpp new file mode 100644 index 000000000..c80d69ce1 --- /dev/null +++ b/src/bin/gs_guc/cluster_config.cpp @@ -0,0 +1,1161 @@ +/***************************************************************************** + * cluster_config.cpp + * PDK工具的分析管理器接口。 + * + * 函数列表: find_gucoption_available + * freefile + * getnodename + * get_local_cordinator_dbpath + * get_local_datanode_dbpath + * get_local_dbpath_by_instancename + * get_local_gtmproxy_dbpath + * get_local_gtm_dbpath + * get_local_gtm_name + * get_local_gtm_proxy_name + * get_local_instancename_by_dbpath + * get_local_num_datanode + * get_nodeidx_by_name + * get_node_nodename + * get_num_nodes + * get_value_in_config_file + * init_gauss_cluster_config + * is_local_node + * is_local_nodeid + * readfile + * + * 标识 + * src/bin/gs_guc/cluster_config.cpp + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/config/cm_config.h" +#include "bin/elog.h" +#include "securec.h" +#include "securec_check.h" +#include "port.h" + +#define MAX_VALUE_LEN 1024 +#define MAX_PARAM_LEN 1024 +#define CLUSTER_CONFIG_SUCCESS 0 +#define CLUSTER_CONFIG_ERROR 1 +#define CM_NODE_NAME_LEN 64 + +#define STADARD_SSH_PORT 22 + +#define STD_FORMAT_ARG_POSITION 2 + +extern char** cndn_param; // 数据节点配置参数数组 +extern char** cmserver_param; // CMServer配置参数数组 +extern char** cmagent_param; // CMAgent配置参数数组 +extern char** gtm_param; // GTM配置参数数组 +extern char** lc_param; // 逻辑复制配置参数数组 +extern char** cndn_guc_info; // 数据节点GUC信息数组 +extern char** cmserver_guc_info; // CMServer GUC信息数组 +extern char** cmagent_guc_info; // CMAgent GUC信息数组 +extern char** gtm_guc_info; // GTM GUC信息数组 +extern char** lc_guc_info; // 逻辑复制 GUC信息数组 +extern int cndn_param_number; // 数据节点配置参数数量 +extern int cmserver_param_number; // CMServer配置参数数量 +extern int cmagent_param_number; // CMAgent配置参数数量 +extern int gtm_param_number; // GTM配置参数数量 +extern int lc_param_number; // 逻辑复制配置参数数量 +extern uint32 g_local_dn_idx; // 本地数据节点索引 +extern char* g_current_data_dir; // 当前数据目录 + +const int g_min_ip_len = 7; // IPV4和IPV6,选择最小长度 + +#ifndef GS_COLLECTOR_BUILD +extern void write_stderr(const char* fmt, ...) // 输出错误信息 +__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, STD_FORMAT_ARG_POSITION))); +#else +#define write_stderr printf +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef enum { + INSTANCE_ANY, + INSTANCE_DATANODE, /* postgresql.conf */ + INSTANCE_COORDINATOR, /* postgresql.conf */ + INSTANCE_GTM, /* gtm.conf */ + INSTANCE_GTM_PROXY, /* gtm_proxy.conf */ + INSTANCE_CMAGENT, /* cm_agent.conf */ + INSTANCE_CMSERVER, /* cm_server.conf */ + INSTANCE_DATAINSTANCE, /* postgresql.conf */ + } NodeType; // 节点类型枚举 + + /* 定义所有节点类型 */ + typedef enum { + GUC_NONE = 0, + GUC_CNDN, // 数据节点 + GUC_GTM, // GTM + GUC_CMSERVER, // CMServer + GUC_CMAGENT, // CMAgent + GUC_LCNAME // 逻辑复制 + } GUC_Node_Type; // GUC节点类型枚举 +// 定义一个常量,表示无效的行索引 + const int INVALID_LINES_IDX = -1; + // 定义一个宏,表示配置文件的名称 +#define GUC_OPT_CONF_FILE "cluster_guc.conf" +// 定义一个常量,表示成功 +#define SUCCESS 0 +// 定义一个常量,表示失败 +#define FAILURE 1 +// 定义一个宏,用于释放指针并将其置为空 +#define GS_FREE(ptr) \ + do { \ + if (NULL != (ptr)) { \ + free((char*)(ptr)); \ + ptr = NULL; \ + } \ + } while (0) + +// 函数:根据数据库路径获取本地实例名称 +// 输入参数:dbpath - 数据库路径,instancename - 实例名称 +// 返回值:int32类型,表示操作结果 + int32 get_local_instancename_by_dbpath(const char* dbpath, char* instancename); + + // 函数:获取本地gtm名称 + // 输入参数:instancename - 实例名称 + // 返回值:int32类型,表示操作结果 + int32 get_local_gtm_name(char* instancename); + + // 函数:获取配置文件中的参数值 + // 输入参数:pg_config_file - 配置文件路径,parameter_in_config - 参数名,para_value - 参数值 + // 返回值:int类型,表示操作结果 + int get_value_in_config_file(const char* pg_config_file, const char* parameter_in_config, char* para_value); + + // 函数:获取所有数据节点的数量 + // 返回值:int类型,表示数据节点数量 + int get_all_datanode_num(); + + // 函数:获取所有协调器的数量 + // 返回值:int类型,表示协调器数量 + int get_all_coordinator_num(); + + // 函数:获取所有cmserver的数量 + // 返回值:int类型,表示cmserver数量 + int get_all_cmserver_num(); + + // 函数:获取所有cmagent的数量 + // 返回值:int类型,表示cmagent数量 + int get_all_cmagent_num(); + + // 函数:获取所有cndn的数量 + // 返回值:int类型,表示cndn数量 + int get_all_cndn_num(); + + // 函数:获取所有gtm的数量 + // 返回值:int类型,表示gtm数量 + int get_all_gtm_num(); + + // 函数:根据节点名称获取AZ的名称 + // 输入参数:nodename - 节点名称 + // 返回值:char*类型,表示AZ的名称 + char* get_AZname_by_nodename(const char* nodename); + + // 函数:在配置文件中查找指定选项可用的行 + // 输入参数:optlines - 配置文件行,opt_name - 选项名,name_offset - 名称偏移量,name_len - 名称长度, + // value_offset - 值偏移量,value_len - 值长度 + // 返回值:int类型,表示操作结果 + int find_gucoption_available( + const char** optlines, const char* opt_name, int* name_offset, int* name_len, int* value_offset, int* value_len); + + // 函数:读取文件内容 + // 输入参数:path - 文件路径,reserve_num_lines - 保留的行数 + // 返回值:char**类型,表示文件的行内容 + char** readfile(const char* path, int reserve_num_lines); + + // 函数:释放文件内容 + // 输入参数:lines - 文件的行内容 + void freefile(char** lines); + + // 函数:获取环境变量的值 + // 输入参数:env_var - 环境变量名,output_env_value - 输出的环境变量值,env_var_value_len - 环境变量值的长度 + // 返回值:bool类型,表示操作结果 + extern bool get_env_value(const char* env_var, char* output_env_value, size_t env_var_value_len); + + // 函数:申请内存空间 + // 输入参数:size - 内存大小 + // 返回值:void*类型,表示申请的内存空间的指针 + void* pg_malloc_memory(size_t size); + + // 函数:复制字符串 + // 输入参数:s - 原始字符串 + // 返回值:char*类型,表示复制后的字符串 + extern char* xstrdup(const char* s); + + // 外部变量:本地实例路径 + extern char* g_local_instance_path; + + // 函数:检查环境变量的值 + // 输入参数:input_env_value - 输入的环境变量值 + extern void check_env_value(const char* input_env_value); + + /* + * 函数:获取本地数据节点数 + * 描述:返回当前节点的数据节点数 + * 输入:无 + * 输出:无 + * 返回:uint32类型,表示数据节点数 + */ + uint32 get_local_num_datanode() + { + return g_currentNode->datanodeCount; + } + + /* + * 函数:获取节点数 + * 描述:返回集群中所有节点的数量 + * 输入:无 + * 输出:无 + * 返回:uint32类型,表示节点数 + */ + uint32 get_num_nodes() + { + return g_node_num; + } + + /* + * 函数:判断输入的节点id是否是当前节点id + * 描述:检查输入的节点id是否等于当前节点id + * 输入:nodeid - 节点id + * 输出:无 + * 返回:bool类型,表示输入的节点id是否是当前节点id + */ + bool is_local_nodeid(uint32 nodeid) + { + return (g_currentNode->node == nodeid); + } + +#ifdef GS_COLLECTOR_BUILD + // 函数:根据节点名称获取节点配置信息 + // 输入参数:name - 节点名称 + // 返回值:staticNodeConfig*指针,表示节点配置信息 + staticNodeConfig* get_node_nodename(char* name); + + /* + * 函数:根据节点名称获取节点配置信息 + * 输入:nodename - 节点名称 + * 输出:无 + * 返回:staticNodeConfig*指针,表示节点配置信息 + */ + staticNodeConfig* get_node_nodename(char* nodename) + { + uint32 nodeidx = 0; + for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { + if (0 == strncmp(g_node[nodeidx].nodeName, nodename, CM_NODE_NAME_LEN)) { + return &g_node[nodeidx]; + } + } + + return NULL; + } + +#endif + + /* + ****************************************************************************** + Function : get_nodeidx_by_name + Description : 根据节点名称获取节点id索引 + Input : nodename - 节点名称 + Output : None + Return : uint32 - 节点id索引 + ****************************************************************************** + */ + int32 get_nodeidx_by_name(const char* nodename) + { + uint32 nodeidx = 0; + for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { // 循环遍历所有节点 + if (0 == strncmp(g_node[nodeidx].nodeName, nodename, CM_NODE_NAME_LEN)) { // 判断节点名称是否匹配 + return (int32)nodeidx; + } + } + + return -1; // 如果未找到对应的节点名称,返回-1 + } + + /* + ****************************************************************************** + Function : get_all_datanode_num + Description : 获取所有数据节点实例数量 + ****************************************************************************** + */ + int get_all_datanode_num() + { + uint32 nodeidx = 0; + int count = 0; + for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { // 循环遍历所有节点 + count += (int)g_node[nodeidx].datanodeCount; // 累加数据节点实例数量 + } + return count; + } + + /* + ****************************************************************************** + Function : get_all_coordinator_num + Description : 获取所有协调器实例数量 + ****************************************************************************** + */ + int get_all_coordinator_num() + { + uint32 nodeidx = 0; + int count = 0; + for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { // 循环遍历所有节点 + if (1 == g_node[nodeidx].coordinate) { // 判断节点是否为协调器 + count += 1; + } + } + return count; + } + + /* + ****************************************************************************** + Function : get_all_cmserver_num + Description : 获取所有CM服务器实例数量 + ****************************************************************************** + */ + int get_all_cmserver_num() + { + uint32 nodeidx; + int count = 0; + for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { // 循环遍历所有节点 + if (1 == g_node[nodeidx].cmServerLevel && g_node[nodeidx].cmDataPath[0] != '\0') { // 判断节点是否为CM服务器 + count += 1; + } + } + + return count; + } + + /* + ****************************************************************************** + Function : get_all_cmagent_num + Description : 获取所有CM代理实例数量 + ****************************************************************************** + */ + int get_all_cmagent_num() + { + return get_num_nodes(); // 直接返回节点数量作为CM代理实例数量 + } + + /* + ****************************************************************************** + Function : get_all_cndn_num + Description : 获取所有CN和DN实例数量 + ****************************************************************************** + */ + int get_all_cndn_num() + { + int count = 0; + count = get_all_datanode_num() + get_all_coordinator_num(); // 数据节点实例数量加上协调器实例数量 + + return count; + } + + /* + ****************************************************************************** + Function : get_all_gtm_num + Description : 获取所有GTM实例数量 + ****************************************************************************** + */ + int get_all_gtm_num() + { + uint32 nodeidx = 0; + int count = 0; + for (nodeidx = 0; nodeidx < get_num_nodes(); nodeidx++) { // 循环遍历所有节点 + if (1 == g_node[nodeidx].gtm && g_node[nodeidx].gtmLocalDataPath[0] != '\0') { // 判断节点是否为GTM + count += 1; + } + } + return count; + } + /* + ******************************************************* + * Function : is_local_node + * Description : 判断给定的节点名是否为当前节点的节点名 + * Input : nodename - 节点名 + * Output : None + * Return : bool - 是否为当前节点的节点名 + ******************************************************* + */ + bool is_local_node(const char* nodename) + { + return (0 == strncmp(g_currentNode->nodeName, nodename, CM_NODE_NAME_LEN)); + } + + /* + ******************************************************* + * Function : getnodename + * Description : 根据节点ID索引获取节点名 + * Input : nodeidx - 节点ID索引 + * Output : None + * Return : char* - 节点名 + ******************************************************* + */ + char* getnodename(uint32 nodeidx) + { + return g_node[nodeidx].nodeName; + } + + /* + ******************************************************* + * Function : get_hostname_or_ip + * Description : 获取主机名或IP地址 + * 如果处于Agent模式,则从/etc/profile中获取HOST_IP环境变量的值作为主机IP。 + * 否则,根据适配性获取主机名。 + * Input : name_len - IP地址或主机名的长度 + * Output : out_name - 从环境变量获取的主机IP或主机名 + * Return : bool - 是否成功获取主机名或IP地址 + ******************************************************* + */ + bool get_hostname_or_ip(char* out_name, size_t name_len) + { + int rc = 0; + char* env_value = NULL; + + if (out_name == NULL) { + (void)write_stderr("ERROR: Get NULL point from upper function when get hostip or hostname.\n"); + return false; + } + + env_value = gs_getenv_r("HOST_IP"); + if (env_value != NULL) { + check_env_value(env_value); + } + + if ((env_value == NULL) || (env_value[0] == '\0')) { + (void)gethostname(out_name, name_len); + if (out_name[0] == '\0') { + return false; + } + } + else { + if (strlen(env_value) >= name_len) { + (void)write_stderr("ERROR: The value of environment variable HOST_IP is too long.\n"); + return false; + } + + if (strlen(env_value) < g_min_ip_len) { + (void)write_stderr("ERROR: The value of environment variable HOST_IP is too short.\n"); + return false; + } + + rc = strcpy_s(out_name, name_len, env_value); + securec_check_c(rc, "\0", "\0"); + } + return true; + } + + /* + ******************************************************* + * Function : get_backIps_by_nodename + * Description : 根据节点名获取后端IP + * Input : nodename - 节点名 + * Output : ipAddress - 后端IP地址 + * Return : ipAddress - 后端IP地址 + ******************************************************* + */ + char* get_backIps_by_nodename(const char* nodename) + { + uint32 nodeidx = 0; + char* ipAddress = NULL; + for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { + if (strcmp(g_node[nodeidx].nodeName, nodename) == 0) { + // 根据cluster_static_config的值进行赋值 + ipAddress = xstrdup(g_node[nodeidx].backIps[0]); + } + } + return ipAddress; + } + + /* + ******************************************************* + * Function : is_instance_in_nodename + * Description : 检查实例是否存在于给定的节点中 + * Input : nodename - 节点名 + * Output : bool - 是否存在实例于给定的节点中 + ******************************************************* + */ + bool is_instance_in_nodename(const char* nodename) + { + uint32 i; + char* backIp = NULL; + + backIp = get_backIps_by_nodename(nodename); + if (NULL == backIp) { + return false; + } + + for (i = 0; i < g_currentNode->datanodeCount; i++) { + if (strcmp(g_currentNode->datanode[i].datanodeLocalDataPath, g_local_instance_path) == 0) { + for (uint32 dnId = 0; dnId < CM_MAX_DATANODE_STANDBY_NUM; dnId++) { + if (strcmp(backIp, g_currentNode->datanode[i].peerDatanodes[dnId].datanodePeerHAIP[0]) == 0) { + GS_FREE(backIp); + return true; + } + } + } + } + GS_FREE(backIp); + return false; + } + /* + ****************************************************************************** + Function : get_AZname_by_nodename + Description : 根据节点名获取 AZ 名字列表 + Input : nodename - 节点名 + Output : azname + Return : azname + 函数 get_AZname_by_nodename 是根据节点名获取 AZ 名字列表的功能。 + 包含的变量有: + - nodename:输入参数,节点名 + + 该函数遍历 g_node 数组,通过比较节点名找到对应的节点,然后将该节点的 azName 字段赋值给 azName 变量。 + + 举例说明:假设 g_node 数组中有以下节点信息: + [{nodeName: "node1", azName : "AZ1"}, { nodeName: "node2", azName : "AZ2" }, { nodeName: "node3", azName : "AZ3" }] + 调用 get_AZname_by_nodename("node2"),返回 "AZ2"。 + + ****************************************************************************** + */ + char* get_AZname_by_nodename(const char* nodename) + { + uint32 nodeidx; + char* azName = NULL; + for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { + if (strcmp(g_node[nodeidx].nodeName, nodename) == 0) { + // 根据 cluster_static_config 值进行赋值 + azName = xstrdup(g_node[nodeidx].azName); + } + } + return azName; + } + + /* + ****************************************************************************** + Function : get_local_dbpath_by_instancename + Description : 根据实例名获取实例所在的目录 + Input : instancename - 实例名 + type - -Z 参数的值 + dbpath - 实例的路径 + Output : None + Return : None + 函数 get_local_dbpath_by_instancename 是根据实例名获取实例所在的目录的功能。 + 包含的变量有: + - instancename:输入参数,实例名 + - type:输入参数, - Z 参数的值 + - dbpath:输出参数,实例的路径 + + 该函数首先判断 type 的值,如果是 INSTANCE_ANY 或 INSTANCE_COORDINATOR,则处理协调器节点的情况。 + 如果 g_currentNode->DataPath 不为空,则调用 get_local_instancename_by_dbpath 函数, + 检查实例名是否与 instancename 相同,如果相同,则将 g_currentNode->DataPath 的值赋给 dbpath 变量,并返回 CLUSTER_CONFIG_SUCCESS。 + + 如果 type 的值是 INSTANCE_ANY 或 INSTANCE_DATANODE,则处理数据节点的情况。遍历 g_currentNode->datanode 数组, + 对每个数据节点的 datanodeLocalDataPath 字段调用 get_local_instancename_by_dbpath 函数,检查实例名是否与 instancename 相同, + 如果相同,则将对应的 datanodeLocalDataPath 的值赋给 dbpath 变量,并返回 CLUSTER_CONFIG_SUCCESS。 + + 如果 type 的值是 INSTANCE_ANY 或 INSTANCE_GTM,则处理 GTM 节点的情况。调用 get_local_gtm_name 函数, + 检查实例名是否与 instancename 相同,如果相同,则将 g_currentNode->gtmLocalDataPath 的值赋给 dbpath 变量,并返回 CLUSTER_CONFIG_SUCCESS。 + + 如果以上情况都不满足,返回 0。 + + 举例说明:假设 g_currentNode 中有以下信息: + - DataPath: "/data" + - datanodeCount : 2 + - datanode : [ + {datanodeLocalDataPath: "/data/dn1"}, + { datanodeLocalDataPath: "/data/dn2" } + ] + 调用 get_local_dbpath_by_instancename("dn2", INSTANCE_DATANODE, dbpath),返回 "/data/dn2"。 + ****************************************************************************** + */ + int32 get_local_dbpath_by_instancename(const char* instancename, const int* type, char* dbpath) + { + uint32 i; + char local_inst_name[CM_NODE_NAME_LEN] = { 0 }; + int32 retval; + errno_t rc = 0; + + if ((*type == INSTANCE_ANY) || (*type == INSTANCE_COORDINATOR)) { + if ('\0' != g_currentNode->DataPath[0]) { + retval = get_local_instancename_by_dbpath(g_currentNode->DataPath, local_inst_name); + if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { + rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->DataPath, CM_PATH_LENGTH); + securec_check_c(rc, "\0", "\0"); + return CLUSTER_CONFIG_SUCCESS; + } + } + } + + if ((*type == INSTANCE_ANY) || (*type == INSTANCE_DATANODE)) { + for (i = 0; i < g_currentNode->datanodeCount; i++) { + retval = + get_local_instancename_by_dbpath(g_currentNode->datanode[i].datanodeLocalDataPath, local_inst_name); + if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { + rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->datanode[i].datanodeLocalDataPath, CM_PATH_LENGTH); + securec_check_c(rc, "\0", "\0"); + return CLUSTER_CONFIG_SUCCESS; + } + } + } + + if ((*type == INSTANCE_ANY) || (*type == INSTANCE_GTM)) { + retval = get_local_gtm_name(local_inst_name); + if ((retval == CLUSTER_CONFIG_SUCCESS) && (0 == strncmp(local_inst_name, instancename, CM_NODE_NAME_LEN))) { + rc = memcpy_s(dbpath, CM_PATH_LENGTH, g_currentNode->gtmLocalDataPath, CM_PATH_LENGTH); + securec_check_c(rc, "\0", "\0"); + return CLUSTER_CONFIG_SUCCESS; + } + } + return 0; + } + /* + ****************************************************************************** + Function : init_gauss_cluster_config + Description : 从集群静态配置文件中获取集群信息 + Input : None + Output : void + Return : None + ****************************************************************************** + */ + int init_gauss_cluster_config(void) + { + char path[MAXPGPATH] = { 0 }; // 存储静态配置文件路径的字符串数组 + char gausshome[MAXPGPATH] = { 0 }; // 存储GAUSSHOME环境变量值的字符串数组 + int err_no = 0; // 存储错误码的整型变量 + int nRet = 0; // 存储返回值的整型变量 + int status = 0; // 存储状态码的整型变量 + uint32 nodeidx = 0; // 存储节点索引的无符号整型变量 + struct stat statbuf {}; // 存储文件stat信息的结构体 + + static bool is_init = false; // 静态变量,用于标记初始化状态 + if (is_init) { + return 0; // 如果已经初始化过了,则直接返回 + } + is_init = true; // 将初始化状态设置为true + g_dn_replication_num = 0; // 将g_dn_replication_num变量初始化为0 + + if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) // 获取GAUSSHOME环境变量的值 + return 1; // 如果获取失败,则返回1 + + check_env_value(gausshome); // 检查环境变量的合法性 + if (NULL != g_lcname) { // 如果g_lcname变量不为空 + nRet = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s.%s", gausshome, g_lcname, STATIC_CONFIG_FILE); // 构建静态配置文件的路径 + } + else { // 如果g_lcname变量为空 + nRet = snprintf_s(path, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, STATIC_CONFIG_FILE); // 构建静态配置文件的路径 + } + securec_check_ss_c(nRet, "\0", "\0"); // 检查字符串格式化的返回值 + + if (checkPath(path) != 0) { // 检查路径是否存在 + write_stderr(_("realpath(%s) failed : %s!\n"), path, strerror(errno)); // 打印错误信息 + return 1; // 返回1表示失败 + } + + if (lstat(path, &statbuf) != 0) { // 获取文件的stat信息 + write_stderr("ERROR: could not stat file \"%s\": %s\n", path, strerror(errno)); // 打印错误信息 + return 1; // 返回1表示失败 + } + + if (NULL != g_lcname) { // 如果g_lcname变量不为空 + status = read_lc_config_file(path, &err_no); // 读取本地配置文件 + } + else { // 如果g_lcname变量为空 + status = read_config_file(path, &err_no); // 读取配置文件 + } + if (0 != status) { // 如果读取配置文件失败 + switch (status) { + case OPEN_FILE_ERROR: { + write_stderr("ERROR: The cluster_staic_config file is not generated or is manually deleted.\n"); // 打印错误信息 + return 1; // 返回1表示失败 + } + case READ_FILE_ERROR: { + write_stderr("ERROR: The cluster_staic_config file permission is insufficient.\n"); // 打印错误信息 + return 1; // 返回1表示失败 + } + case OUT_OF_MEMORY: { + write_stderr("ERROR: The cluster_staic_config open failed cause out of memeory.\n"); // 打印错误信息 + return 1; // 返回1表示失败 + } + default: + break; + } + write_stderr("ERROR: Invalid return value from read_config_file\n"); // 打印错误信息 + return 1; // 返回1表示失败 + } + + if (g_nodeHeader.node <= 0) { // 如果节点id小于等于0 + write_stderr("ERROR: Invalid cluster_staic_config file, curerent node id is:%d .\n", (int32)g_nodeHeader.node); // 打印错误信息 + GS_FREE(g_node); // 释放内存 + return 1; // 返回1表示失败 + } + + for (nodeidx = 0; nodeidx < g_node_num; nodeidx++) { // 遍历节点数组 + if (g_node[nodeidx].node == g_nodeHeader.node) { // 找到当前节点 + g_currentNode = &g_node[nodeidx]; // 设置当前节点指针 + } + } + + if (NULL == g_currentNode) { // 如果当前节点为空指针 + write_stderr("ERROR: failed to find current node by nodeid, curerent node id is:%d .\n", (int32)g_nodeHeader.node); // 打印错误信息 + GS_FREE(g_node); // 释放内存 + return 1; // 返回1表示失败 + } + + if (get_dynamic_dn_role() != 0) { // 获取动态数据节点角色 + write_stderr("ERROR: failed to get dynamic dn role.\n"); // 打印错误信息 + GS_FREE(g_node); // 释放内存 + return 1; // 返回1表示失败 + } + + return 0; // 返回0表示成功 + } + + // 示例 + // 在数据库启动时,需要读取集群静态配置文件,以获取集群信息和节点配置信息。该函数实现了从静态配置文件中读取集群信息的功能, + // 并对读取的结果进行了校验和处理。在读取配置文件之前,需要先获取GAUSSHOME环境变量的值,并构建静态配置文件的路径。 + // 读取配置文件的结果可能有多种情况,分别对应不同的错误码,例如文件打开错误、文件读取错误、内存不足等。如果读取配置文件成功, + // 还需要判断读取到的节点id是否合法,并通过节点id在节点数组中找到当前节点的配置信息。最后,获取动态数据节点的角色。 + // 函数的返回值为0 +/* + * @@GaussDB@@ + * Brief : save_guc_para_info() + * Description : 从cluster_guc.conf文件中获取CN/DN/CMSERVER/CMAGENT的参数 + * Notes : 如果无法打开文件,则返回空指针 + * Input : cluster_guc.conf文件的路径 + * Output : CN/DN/CMSERVER/CMAGENT的配置参数列表 + */ + int save_guc_para_info() + { + int rc = 0; + errno_t ret; + FILE* fp = NULL; + char line_info[MAXPGPATH] = { 0 }; // 存储每一行读取的具体信息 + char temp_line_info[MAXPGPATH] = { 0 }; // 存储临时信息 + char* get_result = NULL; // 存储通过strtok_r函数分割的结果 + char* outer_ptr = NULL; // strtok_r函数使用的外部指针 + GUC_Node_Type type = GUC_NONE; // 存储节点类型 + char gausshome[MAXPGPATH] = { 0 }; // 存储GAUSSHOME路径 + char guc_file[MAXPGPATH] = { 0 }; // 存储cluster_guc.conf文件的路径 + + rc = memset_s(line_info, MAXPGPATH, 0, MAXPGPATH); // 清零line_info内存 + securec_check_c(rc, "\0", "\0"); + rc = memset_s(temp_line_info, MAXPGPATH, 0, MAXPGPATH); // 清零temp_line_info内存 + securec_check_c(rc, "\0", "\0"); + if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) // 获取GAUSSHOME环境变量 + return FAILURE; + + check_env_value(gausshome); // 检查GAUSSHOME合法性 + rc = snprintf_s(guc_file, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, GUC_OPT_CONF_FILE); // 构造cluster_guc.conf文件路径 + securec_check_ss_c(rc, "\0", "\0"); + + if (checkPath(guc_file) != 0) { // 检查文件路径是否存在 + write_stderr(_("realpath(%s) failed : %s!\n"), guc_file, strerror(errno)); // 输出错误信息 + return FAILURE; + } + /* 可能由于权限问题失败 */ + fp = fopen(guc_file, "r"); // 打开cluster_guc.conf文件 + if (fp == NULL) { + write_stderr("ERROR: Failed to open file\"%s\"\n", guc_file); // 输出错误信息 + return FAILURE; + } + if (NULL == fgets(line_info, MAXPGPATH - 1, fp)) { // 读取文件的第一行信息 + write_stderr("ERROR: Failed to read file\"%s\"\n", guc_file); // 输出错误信息 + fclose(fp); + return FAILURE; + } + + while ((fgets(line_info, MAXPGPATH - 1, fp)) != NULL) { // 依次读取文件的每一行 + if ((int)strlen(line_info) > 0) + line_info[(int)strlen(line_info) - 1] = '\0'; // 删除行末的换行符 + else + continue; + + if (line_info[0] == '#') { // 如果是注释行,则跳过 + continue; + } + else if (strncmp(line_info, "[coordinator/datanode]", sizeof("[coordinator/datanode]")) == 0) { // 如果是[coordinator/datanode]节点信息 + type = GUC_CNDN; + continue; + } + else if (strncmp(line_info, "[gtm]", sizeof("[gtm]")) == 0) { // 如果是[gtm]节点信息 + type = GUC_GTM; + continue; + } + else if (strncmp(line_info, "[cmserver]", sizeof("[cmserver]")) == 0) { // 如果是[cmserver]节点信息 + type = GUC_CMSERVER; + continue; + } + else if (strncmp(line_info, "[cmagent]", sizeof("[cmagent]")) == 0) { // 如果是[cmagent]节点信息 + type = GUC_CMAGENT; + continue; + } + else if (strncmp(line_info, "[lcname]", sizeof("[lcname]")) == 0) { // 如果是[lcname]节点信息 + type = GUC_LCNAME; + continue; + } + else if (strncmp(line_info, "[end]", sizeof("[end]")) == 0) { // 如果是[end]节点信息,结束循环 + break; + } + + ret = strcpy_s(temp_line_info, sizeof(temp_line_info), line_info); // 将line_info拷贝到temp_line_info中 + securec_check_c(ret, "\0", "\0"); + get_result = strtok_r(line_info, "|", &outer_ptr); // 使用"|"分割line_info,并将结果存储在get_result中 + if (NULL == get_result) { // 如果分割结果为NULL,则输出错误信息 + write_stderr("ERROR: Line information is incorrect\n"); + fclose(fp); + return FAILURE; + } + + switch (type) { // 根据节点类型进行处理 + case GUC_CNDN: // 如果是[coordinator/datanode]节点信息 + cndn_param[cndn_param_number] = xstrdup(get_result); // 复制并存储参数名称 + cndn_guc_info[cndn_param_number] = xstrdup(temp_line_info); // 复制并存储参数信息 + cndn_param_number++; // 参数计数器加1 + break; + case GUC_GTM: // 如果是[gtm]节点信息 + gtm_param[gtm_param_number] = xstrdup(get_result); // 复制并存储参数名称 + gtm_guc_info[gtm_param_number] = xstrdup(temp_line_info); // 复制并存储参数信息 + gtm_param_number++; // 参数计数器加1 + break; + case GUC_CMSERVER: // 如果是[cmserver]节点信息 + cmserver_param[cmserver_param_number] = xstrdup(get_result); // 复制并存储参数名称 + cmserver_guc_info[cmserver_param_number] = xstrdup(temp_line_info); // 复制并存储参数信息 + cmserver_param_number++; // 参数计数器加1 + break; + case GUC_CMAGENT: // 如果是[cmagent]节点信息 + cmagent_param[cmagent_param_number] = xstrdup(get_result); // 复制并存储参数名称 + cmagent_guc_info[cmagent_param_number] = xstrdup(temp_line_info); // 复制并存储参数信息 + cmagent_param_number++; // 参数计数器加1 + break; + case GUC_LCNAME: // 如果是[lcname]节点信息 + lc_param[lc_param_number] = xstrdup(get_result); // 复制并存储参数名称 + lc_guc_info[lc_param_number] = xstrdup(temp_line_info); // 复制并存储参数信息 + lc_param_number++; // 参数计数器加1 + break; + default: // 默认情况,关闭文件并返回失败 + fclose(fp); + return FAILURE; + } + } + + fclose(fp); // 关闭文件 + return SUCCESS; // 返回成功 + } + */ + + // 示例应用: + // 该函数用于从cluster_guc.conf文件中获取各节点(CN/DN/CMSERVER/CMAGENT)的配置参数。 + // 示例: + // cluster_guc.conf文件内容如下: + // ... + // [coordinator/datanode] + // param1=value1|comment1 + // param2=value2|comment2 + // ... + // [gtm] + // param3=value3|comment3 + // param4=value4|comment4 + // ... + // [cmserver] + // param5=value5|comment5 + // ... + // [cmagent] + // param6=value6|comment6 + // ... + // [lcname] + // param7=value7|comment7 + // ... + // [end] + // ... + // 执行save_guc_para_info()函数后,会将各节点的参数名称和参数信息存储到对应的数组中(如cndn_param、cndn_guc_info),通过返回值判断函数执行是否成功。 + + // 该函数的功能是从cluster_guc.conf文件中读取参数信息并保存。 + // 函数包含以下变量及功能: + // - rc: 整型变量,存储memset_s函数的返回值 + // - ret: errno_t类型变量,存储strcpy_s函数的返回值 + // - fp: FILE指针变量,用于存储打开的cluster_guc.conf文件的指针 + // - line_info: 字符数组,用于存储每一行读取的具体信息 + // - temp_line_info: 字符数组,用于存储临时信息 + // - get_result: 字符指针,通过strtok_r函数分割得到的结果 + // - outer_ptr: 字符指针,strtok_r函数使用的外部指针 + // - type: GUC_Node_Type枚举类型,用于存储节点类型 + // - gausshome: 字符数组,用于存储GAUSSHOME路径 + // - guc_file: 字符数组,用于存储cluster_guc.conf文件的路径 + + // 在代码中,首先使用memset_s函数将line_info和temp_line_info清零。 + // 然后通过get_env_value函数获取GAUSSHOME环境变量,如果获取失败则返回FAILURE。 + // 接着使用check_env_value函数检查GAUSSHOME合法性。 + // 通过snprintf_s函数构造cluster_guc.conf文件的路径。 + // 调用checkPath函数检查文件路径是否存在,如果不存在则输出错误信息并返回FAILURE。 + // 使用fopen函数打开cluster_guc.conf文件,如果打开失败则输出错误信息并返回FAILURE。 + // 调用fgets函数读取文件的第一行信息,如果读取失败则输出错误信息并关闭文件返回FAILURE。 + // 进入while循环,逐行读取文件信息。 + // 对于每一行信息,首先删除行末的换行符。 + // 判断行的类型,如果是注释行则跳过。 + // 如果是各节点信息([coordinator/datanode]、[gtm]、[cmserver]、[cmagent]、[lcname]),则设置type为对应的节点类型,然后继续下一行的读取。 + // 如果是[end]节点信息,则循环结束。 + // 将line_info拷贝到temp_line_info中。 + // 使用strtok_r函数分割line_info,用"|"作为分隔符,分割得到的结果存储到get_result中。 + // 如果get_result为NULL,则输出错误信息并关闭文件返回FAILURE。 + // 根据节点类型,将参数名称和参数信息分别复制并存储到对应的数组中。 + // 循环结束后,关闭文件并返回SUCCESS。 +/* + * @@GaussDB@@ + * Brief :readfile(const char* path, int reserve_num_lines) + * Description :从文件中读取值 + * Notes :如果无法打开文件,则返回NULL + */ + char** readfile(const char* path, int reserve_num_lines) + { + int fd; + int nlines = 0; // 文件的行数 + 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; + + /* + * 将整个文件读入内存。 + * + * 文件可能会同时发生更改,因此我们将整个文件一次性读入内存中, + * 使用单个read()调用。虽然不能保证得到一个原子快照, + * 但实际上,对于小文件,这足够接近当前的使用情况了。 + */ + fd = open(path, O_RDONLY | PG_BINARY, 0); + if (fd < 0) { + return NULL; + } + if (fstat(fd, &statbuf) < 0) { + close(fd); + return NULL; + } + if (statbuf.st_size == 0) { + /* 空文件 */ + close(fd); + result = (char**)malloc((1 + reserve_num_lines) * sizeof(char*)); + if (NULL == result) { + write_stderr("ERROR: Memory allocation failed.\n"); + return NULL; + } + + for (i = 0; i < reserve_num_lines + 1; i++) { + result[i] = NULL; + } + + *result = NULL; + return result; + } + + if (statbuf.st_size > LONG_MAX - 1) { + write_stderr("malloc size too big, size (%ld).\n", statbuf.st_size); + close(fd); + return NULL; + } + + buffer = (char*)malloc((size_t)(statbuf.st_size + 1)); + if (NULL == buffer) { + close(fd); + write_stderr("ERROR: Memory allocation failed.\n"); + return NULL; + } + + len = read(fd, buffer, statbuf.st_size + 1); + close(fd); + if (len != statbuf.st_size) { + /* 哎呀,fstat和read之间的文件大小发生了变化 */ + write_stderr("ERROR: File is buzy read failed.\n"); + GS_FREE(buffer); + return NULL; + } + + /* + * 计算行数。我们期望每行后面都有一个换行符, + * 包括文件末尾的换行符。如果文件末尾没有换行符, + * 最后一个换行符之后的任何字符将被忽略。 + */ + nlines = 0; + for (i = 0; i < len; i++) { + if (buffer[i] == '\n') { + nlines++; + } + } + + /* 设置结果缓冲区 */ + result = (char**)malloc((nlines + 1 + reserve_num_lines) * sizeof(char*)); + if (NULL == result) { + GS_FREE(buffer); + write_stderr("ERROR: Memory allocation failed.\n"); + return NULL; + } + + /* 现在将缓冲区拆分成行 */ + linebegin = buffer; + n = 0; + for (i = 0; i < len; i++) { + if (buffer[i] == '\n') { + int slen = &buffer[i] - linebegin + 1; + char* linebuf = (char*)malloc(slen + 1); + if (NULL == linebuf) { + write_stderr("ERROR: Memory allocation failed.\n"); + for (i = 0; i < n; i++) { + GS_FREE(result[i]); + } + GS_FREE(result); + GS_FREE(buffer); + return NULL; + } + rc = memcpy_s(linebuf, slen, linebegin, slen); + securec_check_c(rc, "\0", "\0"); + linebuf[slen] = '\0'; + result[n++] = linebuf; + linebegin = &buffer[i + 1]; + } + } + result[n] = NULL; + + for (i = 0; i < reserve_num_lines; i++) { + result[n + i] = NULL; + } + + GS_FREE(buffer); + + return result; + } + + /******************************************************************************* + Function : get_value_in_config_file + Description : 获取配置文件中的值 + Input : pg_config_file - 配置文件名,例如:postgresql.conf/gtm.conf + parameter_in_config - 配置文件中的参数名,例如:dn_6002_6003 + para_value - 配置文件中参数的值 + Output : None + Return : None + *******************************************************************************/ + int get_value_in_config_file(const char* pg_config_file, const char* parameter_in_config, char* para_value) + { + int values_offset = 0; // 参数值在行中的偏移量 + int values_len = 0; // 参数值的长度 + int values_line = 0; // 参数所在行的索引 + char** all_lines = NULL; // 存储文件中所有行的数组 + int rc = 0; + + all_lines = readfile(pg_config_file, 0); + if (NULL == all_lines) { + return 1; + } + values_line = + find_gucoption_available((const char**)all_lines, parameter_in_config, NULL, NULL, &values_offset, &values_len); + + if (values_line != INVALID_LINES_IDX) { + rc = strncpy_s(para_value, + MAX_VALUE_LEN, + all_lines[values_line] + values_offset + 1, + (size_t)Min(values_len - 2, MAX_VALUE_LEN - 1)); + securec_check_c(rc, "\0", "\0"); + } + + freefile(all_lines); + + return (values_line == INVALID_LINES_IDX); + } + + //解析: + // 1. 函数readfile用于从文件中读取值。参数path表示文件路径,reserve_num_lines表示保留的行数。函数返回一个字符串数组,存储文件中的数据。如果无法打开文件,返回NULL。 + // 2. 函数get_value_in_config_file用于获取配置文件中的值。参数pg_config_file表示配置文件名,parameter_in_config表示配置文件中的参数名,para_value表示参数值。函数通过调用readfile函数读取配置文件的所有行,然后查找参数所在的行,并将参数值拷贝到para_value中。 + // 3. 代码中的注释解释了函数的功能和每个变量的用途。 + // 4. 代码中的语言块功能被详细解释,包括打开文件、读取文件、计算行数、拆分缓冲区等操作。 + // 5. 函数get_value_in_config_file中的实例应用是读取配置文件中的参数值,可以用于读取数据库配置文件中的各种参数值,如缓冲区大小、连接数限制等。 + if (NULL == optlines || NULL == opt_name) { + return INVALID_LINES_IDX; + } + + // 计算gucoption名称的长度 + 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 = (char*)optlines[i]; + + // 跳过空格 + while (isspace((unsigned char)*p)) { + p++; + } + + // 比较gucoption名称 + if (strncmp(p, opt_name, paramlen) != 0) { + continue; + } + + // 记录gucoption名称的偏移量 + 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; + + // 查找gucoption值的末尾位置 + while (*q && !(*q == '\n' || *q == '#')) { + if (!isspace((unsigned char)*q)) { + tmp = ++q; + } + else { + q++; + } + } + + // 记录gucoption值的偏移量 + if (value_offset != NULL) { + *value_offset = p - optlines[i]; + } + + // 记录gucoption值的长度 + if (value_len != NULL) { + *value_len = (NULL == tmp) ? 0 : (tmp - p); + } + + return i; + } + + return INVALID_LINES_IDX; -- 2.34.1 From 1fd2a7a10f15ec98154b494962c5998d111fc2d1 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:04:35 +0800 Subject: [PATCH 13/56] Delete 'src/bin/gs_guc/cluster_guc.cpp' --- src/bin/gs_guc/cluster_guc.cpp | 4522 -------------------------------- 1 file changed, 4522 deletions(-) delete mode 100644 src/bin/gs_guc/cluster_guc.cpp diff --git a/src/bin/gs_guc/cluster_guc.cpp b/src/bin/gs_guc/cluster_guc.cpp deleted file mode 100644 index 52a82818a..000000000 --- a/src/bin/gs_guc/cluster_guc.cpp +++ /dev/null @@ -1,4522 +0,0 @@ -/* - * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group - * - * 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. - * --------------------------------------------------------------------------------------- - * - * cluster_guc.cpp - * Interfaces for analysis manager of PDK tool. - * - * Function List: execute_guc_command_in_remote_node - * form_commandline_options - * get_instance_type - * process_cluster_guc_option - * validate_cluster_guc_options - * - * IDENTIFICATION - * src/bin/gs_guc/cluster_guc.cpp - * - * --------------------------------------------------------------------------------------- - */ - -#include "postgres_fe.h" -#include "libpq/libpq-fe.h" -#include "bin/elog.h" -#include "pg_config.h" -#include "common/config/cm_config.h" -#include -#include - -const int CLUSTER_CONFIG_SUCCESS = 0; -const int CLUSTER_CONFIG_ERROR = 1; -#define LOOP_COUNT 3 -#define DOUBLE_PRECISE 0.000000001 -#define MAX_HOST_NAME_LENGTH 255 -#define LARGE_INSTANCE_NUM 2 -#define CM_NODE_NAME_LEN 64 -#define STATIC_CONFIG_FILE "cluster_static_config" -#define SSH_OPTIONS \ - "-o BatchMode=yes -o TCPKeepAlive=yes -o ServerAliveInterval=15 -o ServerAliveCountMax=4 -o ConnectTimeout=5 -o " \ - "ConnectionAttempts=6" -#define GS_FREE(ptr) \ - do { \ - if (NULL != (ptr)) { \ - free((char*)(ptr)); \ - ptr = NULL; \ - } \ - } while (0) - -#define PROCESS_STATUS(status) \ - do { \ - if (status == OUT_OF_MEMORY) { \ - write_stderr("Failed: out of memory\n"); \ - exit(1); \ - } \ - if (status == OPEN_FILE_ERROR) { \ - write_stderr("Failed: cannot find the expected data dir\n"); \ - exit(1); \ - } \ - } while (0) - -const int GTM_INSTANCE_LEN = 3; // eg: one -const int CN_INSTANCE_LEN = 7; // eg: cn_5001 -const int DN_INSTANCE_LEN = 12; // eg: dn_6001_6002 - -extern char** config_param; -extern char** config_value; -extern int config_param_number; -extern bool is_hba_conf; -extern int node_type_number; - -extern bool g_need_changed; -extern char* g_local_instance_path; - -typedef struct { - char** nodename_array; - char** gucinfo_array; - char** paramname_array; - char** paramvalue_array; - uint32 nodename_num; - uint32 gucinfo_num; - uint32 paramname_num; - uint32 paramvalue_num; -} gucInfo; - -typedef struct { - char** nodename_array; - uint32 num; -} nodeInfo; - -#define MAX_P_READ_BUF 1024 -typedef struct tag_pcommand { - FILE* pfp; - char readbuf[MAX_P_READ_BUF]; - int cur_buf_loc; - char* nodename; - int retvalue; -} PARALLEL_COMMAND_S; - -PARALLEL_COMMAND_S* g_parallel_command_cxt = NULL; -static int g_max_commands_parallel = 0; -static int g_cur_commands_parallel = 0; - -/* real result */ -extern gucInfo* g_real_gucInfo; -/* expect result */ -extern gucInfo* g_expect_gucInfo; - -extern char gucconf_file[MAXPGPATH]; -extern int config_param_number; -extern char** config_param; -extern char** config_value; - -extern int config_param_number; -extern int cndn_param_number; -extern int cmserver_param_number; -extern int cmagent_param_number; -extern int gtm_param_number; -extern int lc_param_number; -extern int config_value_number; -extern int node_type_number; -extern int arraysize; -extern char** cndn_param; -extern char** gtm_param; -extern char** cmserver_param; -extern char** cmagent_param; -extern char** lc_param; -extern char** cndn_guc_info; -extern char** cmserver_guc_info; -extern char** cmagent_guc_info; -extern char** gtm_guc_info; -extern char** lc_guc_info; -extern const char* progname; - -/* status which perform remote connection */ -extern bool g_remote_connection_signal; -/* result which perform remote command */ -extern unsigned int g_remote_command_result; - -/* storage the name which perform remote connection failed */ -extern nodeInfo* g_incorrect_nodeInfo; -/* storage the name which need to ignore */ -extern nodeInfo* g_ignore_nodeInfo; - -typedef enum { - NO_COMMAND = 0, - SET_CONF_COMMAND, - RELOAD_CONF_COMMAND, - ENCRYPT_KEY_COMMAND, - CHECK_CONF_COMMAND -} CtlCommand; -extern CtlCommand ctl_command; - -typedef enum { - INSTANCE_ANY, - INSTANCE_DATANODE, /* postgresql.conf */ - INSTANCE_COORDINATOR, /* postgresql.conf */ - INSTANCE_GTM, /* gtm.conf */ - INSTANCE_GTM_PROXY, /* gtm_proxy.conf */ - INSTANCE_CMAGENT, /* cm_agent.conf */ - INSTANCE_CMSERVER, /* cm_server.conf */ -} NodeType; - -/* transform unit */ -const int KB_PER_MB = 1024; -#define KB_PER_GB (1024 * 1024) -const int MB_PER_GB = 1024; -#define MS_PER_S 1000 -#define MS_PER_MIN (1000 * 60) -#define MS_PER_H (1000 * 60 * 60) -#define MS_PER_D (1000 * 60 * 60 * 24) -#define S_PER_MIN 60 -#define S_PER_H (60 * 60) -#define S_PER_D (60 * 60 * 24) -#define MIN_PER_H 60 -#define MIN_PER_D (60 * 24) -#define H_PER_D 24 - -/* execute result */ -#define SUCCESS 0 -#define FAILURE 1 - -#define MAX_LINE_LEN 8192 -#define MAX_MESG_LEN 4096 -#define MAX_PARAM_LEN 1024 -#define MAX_VALUE_LEN 1024 -#define MAX_UNIT_LEN 8 -#define MAX_INSTANCENAME_LEN 128 -#define GUC_OPT_CONF_FILE "cluster_guc.conf" - -bool is_disable_log_directory = false; - -/* - type about all guc options - */ -typedef enum { GUC_ERROR = -1, GUC_NAME, GUC_TYPE, GUC_VALUE, GUC_MESG } OptType; - -/* type about all guc unit */ -/* - ********************************************* - parameters value support units - ********************************************* - * type_name units_type numbers - ********************************************* - * real units_d 3 - * integer units_kB 26 - * integer units_MB 3 - * integer units_ms 9 - * integer units_s 19 - * integer units_min 4 - * integer units_d 1 - ********************************************* -*/ -typedef enum { UNIT_ERROR = -1, UNIT_KB, UNIT_MB, UNIT_GB, UNIT_MS, UNIT_S, UNIT_MIN, UNIT_H, UNIT_D } UnitType; - -/* type about all guc parameters */ -typedef enum { - GUC_PARA_ERROR = -1, - GUC_PARA_BOOL, /* bool */ - GUC_PARA_ENUM, /* enum */ - GUC_PARA_INT, /* int */ - GUC_PARA_REAL, /* real */ - GUC_PARA_STRING /* string */ -} GucParaType; - -struct guc_config_enum_entry { - char guc_name[MAX_PARAM_LEN]; - GucParaType type; - char guc_value[MAX_VALUE_LEN]; - char guc_unit[MAX_UNIT_LEN]; - char message[MAX_MESG_LEN]; -}; - -struct guc_minmax_value { - char min_val_str[MAX_VALUE_LEN]; - char max_val_str[MAX_VALUE_LEN]; -}; - -/* value about bool type */ -const char* guc_bool_valuelist[] = { - "true", - "false", - "on", - "off", - "yes", - "no", - "0", - "1", -}; - -/* value type list */ -const char *value_type_list[] = { - "boolean", - "enum", - "integer", - "real", - "string", -}; - -/* value about the parameters which unit is 8kB */ -const char* unit_eight_kB_parameter_list[] = { - "backwrite_quantity", - "effective_cache_size", - "prefetch_quantity", - "segment_size", - "shared_buffers", - "temp_buffers", - "wal_buffers", - "wal_segment_size", -}; -/* the size of page, unit is kB */ -#define PAGE_SIZE 8 - -int process_guc_command(const char* datadir); -void do_checkvalidate(int type); -void get_instance_configfile(const char* datadir); -char* get_ctl_command_type(); -void* pg_malloc(size_t size); -void* pg_malloc_zero(size_t size); -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -int execute_guc_command_in_remote_node(int idx, char* command); -static char* form_commandline_options(const char* instance_name, const char* indatadir, bool local_mode); -uint32 get_num_nodes(); -uint32 get_local_num_datanode(); -bool is_local_nodeid(uint32 nodeid); -bool is_local_node(char* nodename); -int32 get_nodeidx_by_name(char* nodename); -int get_local_dbpath_by_instancename(const char* instancename, int* type, char* dbpath); -int init_gauss_cluster_config(void); - -extern NodeType nodetype; -char* getnodename(uint32 nodeidx); -bool get_hostname_or_ip(char* out_name, size_t name_len); -int32 get_local_instancename_by_dbpath(char* dbpath, char* instancename); -char* xstrdup(const char* s); -char** readfile(const char* path, int reserve_num_lines); -void freefile(char** lines); -bool get_env_value(const char* env_var, char* output_env_value, size_t env_var_value_len); -int get_all_datanode_num(); -int get_all_coordinator_num(); -int get_all_cmserver_num(); -int get_all_cmagent_num(); -int get_all_cndn_num(); -int get_all_gtm_num(); -char* get_AZ_value(const char* value, const char* data_dir); -char* get_AZname_by_nodename(char* nodename); - -void make_string_tolower(const char* source, char* dest, const int destlen); - -void save_expect_instance_info(const char* datadir); -void save_remote_instance_info( - const char* result_file, const char* nodename, char* command, gucInfo* guc_info, bool isRealGucInfo); - -void do_local_instance(int type, char* instance_name, char* indatadir); -void do_remote_instance(char* nodename, const char* instance_name, const char* indatadir); -void do_all_nodes_instance(const char* instance_name, const char* indatadir); - -void check_env_value(const char* input_env_value); - -/* *********************************************************************************** */ -GucParaType get_guc_type(const char* type); -UnitType get_guc_unit(const char* unit); -int do_local_para_value_change(int type, char* datadir); -int do_local_guc_command(int type, char* temp_datadir); -char** get_guc_option(); -int do_gucopt_parse(const char* guc_opt, struct guc_config_enum_entry& guc_variable_list); -int check_parameter(int type); -int check_parameter_value( - const char* paraname, GucParaType type, char* guc_list_value, const char* guc_list_unit, const char* value); -int check_parameter_name(char** guc_opt, int type); -bool check_parameter_is_valid(int type); -int parse_value(const char* paraname, const char* value, const char* guc_list_unit, int64* result_int, - double* result_double, bool isInt); -int get_guc_minmax_value(const char* guc_list_val, struct guc_minmax_value& value_list); -int check_int_real_type_value( - const char* paraname, const char* guc_list_value, const char* guc_list_unit, const char* value, bool isInt); -int check_enum_type_value(const char* paraname, char* guc_list_value, const char* value); -int check_bool_type_value(const char* value); -int check_string_type_value(const char* paraname, const char* value); - -void do_command_for_cn_gtm(int type, char* indatadir, bool isCoordinator); -void do_command_for_dn(int type, char* indatadir); -void do_command_for_cm(int type, char* indatadir); -void do_command_for_cndn(int type, char* indatadir); -char *get_cm_real_path(int type); -void create_tmp_dir(const char* pathdir); -void remove_tmp_dir(const char* pathdir); -bool is_record(int type, char* flag_str); -bool compare_str(char* src_str, char* start_str, char* end_str); -void do_command_with_instance_name_option_local(int type, char* instance_name); -void do_remote_instance_local(char* nodename, const char* instance_name, const char* indatadir); -void do_all_nodes_instance_local(const char* instance_name, const char* indatadir); -void do_all_nodes_instance_local_in_serial(const char* instance_name, const char* indatadir); -void do_all_nodes_instance_local_in_parallel(const char* instance_name, const char* indatadir); -char** get_guc_line_info(const char** line); -static char* GetEnvStr(const char* env); -static void executePopenCommandsParallel(const char* cmd, int idx, bool is_local_node); -static void readPopenOutputParallel(const char* cmd, bool if_for_all_instance); -static void SleepInMilliSec(uint32_t sleepMs); -static void init_global_command(); -static void reset_global_command(); -static void do_all_nodes_instance_local_in_parallel_loop(const char* instance_name, const char* indatadir); -/******************************************************************************* - Function : xstrdup - Description : - Input : - Output : src string - Return : dest string - ***************************************************************************** -*/ -char* xstrdup(const char* s) -{ - char* result = NULL; - - result = strdup(s); - if (NULL == result) { - (void)write_stderr(_("%s: out of memory\n"), "gs_guc"); - exit(1); - } - return result; -} -/* - ****************************************************************************** - Function : make_string_tolower - Description : copy source to dest, and make all alpha about dest to lower. - Input : source -- source string - dest -- dest string - Output : void - Return : void - ***************************************************************************** -*/ -void make_string_tolower(const char* source, char* dest, const int destlen) -{ - int i = 0; - int len = (int)strlen(source); - if (len > destlen) { - len = destlen; - } - for (i = 0; i < len; i++) - dest[i] = tolower(source[i]); - dest[i] = '\0'; -} -/* - ****************************************************************************** - Function : get_instance_type - Description : - Input : - Output : None - Return : None - ****************************************************************************** -*/ -const char* get_instance_type() -{ - char* type = NULL; - switch (nodetype) { - case INSTANCE_COORDINATOR: { - type = "-Z coordinator"; - break; - } - case INSTANCE_DATANODE: { -#ifdef ENABLE_MULTIPLE_NODES - type = "-Z datanode"; -#else - type = ""; -#endif - break; - } - case INSTANCE_CMSERVER: { - type = "-Z cmserver"; - break; - } - case INSTANCE_CMAGENT: { - type = "-Z cmagent"; - break; - } - case INSTANCE_GTM: { - type = "-Z gtm"; - break; - } - default: { - type = ""; - break; - } - } - return (const char*)type; -} - -/* - ****************************************************************************** - Function : modify_parameter_value - Description : If parameter value have the special character '$', when do remote setting - we should changed the parameter value first. - Input : value parameter value - : localMode do it on local node - Return : char * - Warning : this function will malloc a buffer for returned value, and won't free in this function. - so the caller should free this buffer after use this function's returned value. - ****************************************************************************** -*/ -char* modify_parameter_value(const char* value, bool localMode) -{ - int i = 0; - int j = 0; - int k = 0; - int backslash_num = 0; - const int local_backslash_num = 1; - const int remote_backslash_num = 3; - - char* buffer = (char*)pg_malloc_zero(MAX_VALUE_LEN * sizeof(char)); - - for (i = 0, j = 0; i < (int)strlen(value) && j < MAX_VALUE_LEN; i++, j++) { - if (value[i] == '$') { - /* - * If value have the special character '$', adding backslash before '$' is different between local - * command and remote command. when do remote setting, the commands like this: remote command: ssh -n - * nodename "gs_guc set -Z datanode -I all -c \"dynamic_library_path='\\\$libdir/xxx'\"" local command: - * gs_guc set -Z datanode -I all -c \"dynamic_library_path='\$libdir/xxx'\"" - */ - backslash_num = localMode ? local_backslash_num : remote_backslash_num; - for (k = 0; k < backslash_num && j < MAX_VALUE_LEN; k++) { - buffer[j] = '\\'; - j++; - } - if (j >= MAX_VALUE_LEN) { - write_stderr(_("%s: out of memory\n"), progname); - exit(1); - } - buffer[j] = value[i]; - } else { - buffer[j] = value[i]; - } - } - return buffer; -} - -/* - ****************************************************************************** - Function : form_commandline_options - Description : Generate the complete guc command - Input : instance_name - the instance name - indatadir - the path of instance - local_mode - local mode or not - Output : None - Return : None - ****************************************************************************** -*/ -static char* form_commandline_options(const char* instance_name, const char* indatadir, bool local_mode) -{ - char* buffer = NULL; - int buflen = 0; - int curlen = 0; - int i = 0; - int nRet = 0; - /* a variable that storage new parameter value*/ - char* new_value = NULL; - - /* other standard options */ -#define MIN_COMMAND_LEN 256 - - /* adding -c + '=' + two ' ' + two '\' + two '"' */ -#define ALLIG_POSTGRES_CONF_LEN 20 - - /* adding -h + two '\' + two '"' */ -#define ALLIG_HBA_CONF_LEN 10 - - /* -N options is not required */ - buflen = MIN_COMMAND_LEN; - if (instance_name != NULL) { - buflen += strlen(instance_name); - } else { - buflen += strlen(indatadir); - } - - /* find length required for options */ - for (i = 0; i < config_param_number; i++) { - if (!is_hba_conf) { - buflen += (ALLIG_POSTGRES_CONF_LEN + strlen(config_param[i])); - if (config_value[i] != NULL) { - buflen += strlen(config_value[i]); - } - } else { - buflen += ALLIG_HBA_CONF_LEN; - if (config_value[i] != NULL) { - buflen += strlen(config_value[i]); - } - } - } - - buffer = (char*)pg_malloc_zero(buflen); - - /* SET / RESET [--cordinator --datanode --gtm ] */ - curlen = snprintf_s( - buffer, buflen, buflen - 1, "gs_guc %s %s ", get_ctl_command_type(), get_instance_type()); - - securec_check_ss_c(curlen, buffer, "\0"); - if (nodetype == INSTANCE_CMAGENT || - nodetype == INSTANCE_CMSERVER) { - nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), "-D \"cm_instance_data_path\""); - } else { - /* -I or -D */ - if (NULL != instance_name) { - nRet = snprintf_s(buffer + curlen , (buflen - curlen), (buflen - curlen - 1), "-I %s", - instance_name); - } else { - nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), "-D \"%s\"", - indatadir); - } - } - securec_check_ss_c(nRet, buffer, "\0"); - curlen = curlen + nRet; - /* -c options */ - for (i = 0; i < config_param_number; i++) { - if (!is_hba_conf) { - /* The parameter name does not has special character '$'. - * So We need to give attention to the parameter value. - */ - if (config_value[i] != NULL) { - new_value = modify_parameter_value(config_value[i], local_mode); - if (local_mode) { - nRet = snprintf_s(buffer + curlen, - (buflen - curlen), - (buflen - curlen - 1), - " -c %c%s=%s%c", - '"', - config_param[i], - new_value, - '"'); - } else { - nRet = snprintf_s(buffer + curlen, - (buflen - curlen), - (buflen - curlen - 1), - " -c \\\"%s=%s\\\"", - config_param[i], - new_value); - } - GS_FREE(new_value); - } else { - nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), " -c %s", config_param[i]); - } - securec_check_ss_c(nRet, buffer, "\0"); - curlen = curlen + nRet; - } else { - if (local_mode) { - nRet = snprintf_s( - buffer + curlen, (buflen - curlen), (buflen - curlen - 1), " -h %c%s%c", '"', config_value[i], '"'); - } else { - nRet = snprintf_s( - buffer + curlen, (buflen - curlen), (buflen - curlen - 1), " -h \\\"%s\\\"", config_value[i]); - } - securec_check_ss_c(nRet, buffer, "\0"); - curlen = curlen + nRet; - } - } - - return buffer; -} - -/* - ****************************************************************************** - Function : get_nodeidx_by_HA - Description : get the node index by HA ip/port - Input : HAIp - HA ipaddr - : HAPort HA port - Output : None - Return : int - node id index - ******************************************************************************* -*/ -uint32 get_nodeidx_by_HA(const char* HAIp, uint32 HAPort) -{ - uint32 i = 0; - uint32 j = 0; - - for (i = 0; i < g_node_num; i++) { - for (j = 0; j < g_node[i].datanodeCount; j++) { - if ((0 == strncmp(g_node[i].datanode[j].datanodeLocalHAIP[0], HAIp, strlen(HAIp))) && - (0 == g_node[i].datanode[j].datanodeLocalHAPort - HAPort)) - return i; - } - } - return 0; -} -/* - ****************************************************************************** - Function : get_instance_id - Description : get_instance_id by data path and HA ip/port. First, get node index by HA ip/port; - then get instance id by data path - Input : dataPath - datanode instance path - : HAIp - HA ipaddr - : HAPort - HA port - Output : None - Return : int - datanode instance id - ****************************************************************************** -*/ -uint32 get_instance_id(const char* dataPath, const char* HAIp, uint32 HAPort) -{ - uint32 i = 0; - uint32 nodeidx = 0; - - nodeidx = get_nodeidx_by_HA(HAIp, HAPort); - for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { - if (0 == strncmp(g_node[nodeidx].datanode[i].datanodeLocalDataPath, dataPath, strlen(dataPath))) - return g_node[nodeidx].datanode[i].datanodeId; - } - return 0; -} - -/* - ****************************************************************************** - Function : is_instance_level_correct - Description : check whether the instance is in same safety ring by data path , HA ip/port and the level. First, get -node index by HA ip/port; then check the level Input : dataPath - datanode instance path : HAIp - HA ipaddr : -HAPort - HA port : level - the instance level Output : None Return : True/False - ****************************************************************************** -*/ -bool is_instance_level_correct(const char* dataPath, const char* HAIp, uint32 HAPort, uint32 level) -{ - uint32 i = 0; - uint32 nodeidx = 0; - /*get node idx by HA information */ - nodeidx = get_nodeidx_by_HA(HAIp, HAPort); - for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { - if ((0 == strncmp(g_node[nodeidx].datanode[i].datanodeLocalDataPath, dataPath, strlen(dataPath))) && - (level == g_node[nodeidx].datanode[i].datanodeRole)) - return true; - } - return false; -} - -/* - ****************************************************************************** -GetPgxcNodeNameForMasterDnInstance - get the pgxc_node_name on single primary mutile standby cluster, by node index and datanode instance index -Input: nodeidx -> node index(it comes from static_config_file) - instanceidx -> node index(it comes from static_config_file) - ****************************************************************************** -*/ -char* GetPgxcNodeNameForMasterDnInstance(int32 nodeidx, int32 instanceidx) -{ - char* pgxcNodeName = (char*)pg_malloc_zero(sizeof(char) * MAXPGPATH); - int ret = 0; - - /* - * get all standby dn instance id arry. - * name dn_6001_6002_6003 -> 6001 must be primary DN instance - */ - uint32 instance_id_arry[CM_NODE_MAXNUM] = {0}; - - ret = snprintf_s(pgxcNodeName, MAXPGPATH, MAXPGPATH - 1, "dn_%u", g_node[nodeidx].datanode[instanceidx].datanodeId); - securec_check_ss_c(ret, "\0", "\0"); - - if (g_dn_replication_num == 0) { - write_stderr("ERROR: Failed to get dn instance in the partition.\n"); - exit(1); - } - - for (uint32 dnId = 0; dnId < g_dn_replication_num - 1; dnId++) { - char tmp_command[MAXPGPATH] = {0}; - /* - * The instance must be standby. So use datanodePeerHAIP replace datanodePeer2HAIP - */ - instance_id_arry[dnId] = - get_instance_id(g_node[nodeidx].datanode[instanceidx].peerDatanodes[dnId].datanodePeerDataPath, - g_node[nodeidx].datanode[instanceidx].peerDatanodes[dnId].datanodePeerHAIP[0], - g_node[nodeidx].datanode[instanceidx].peerDatanodes[dnId].datanodePeerHAPort); - ret = memset_s(tmp_command, MAXPGPATH, '\0', MAXPGPATH); - securec_check_c(ret, "\0", "\0"); - ret = snprintf_s(tmp_command, MAXPGPATH, MAXPGPATH - 1, "_%u", instance_id_arry[dnId]); - securec_check_ss_c(ret, "\0", "\0"); - ret = strncat_s(pgxcNodeName, MAXPGPATH, tmp_command, strlen(tmp_command)); - securec_check_ss_c(ret, "\0", "\0"); - } - - return pgxcNodeName; -} - -/* - ****************************************************************************** - CheckInstanceNameForSinglePrimaryMutilStandby - check the instance_name, whether it is in single primary mutile standby cluster or not - instance Type info: consist with OM - PRIMARY_DN 0 - STANDBY_DN 1 - DUMMY_STANDBY_DN 2 - ****************************************************************************** -*/ -bool CheckInstanceNameForSinglePrimaryMutilStandby(int32 nodeidx, const char* instance_name) -{ - uint32 i = 0; - uint32 j = 0; - int ret = 0; - uint32 nameLen = 0; - char* pgxcNodeName = NULL; - - for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { - /* deal with the primary dn instance branch */ - if (g_node[nodeidx].datanode[i].datanodeRole == 0) { - pgxcNodeName = GetPgxcNodeNameForMasterDnInstance(nodeidx, i); - nameLen = strlen(instance_name) > strlen(pgxcNodeName) ? strlen(instance_name) : strlen(pgxcNodeName); - ret = strncmp(pgxcNodeName, instance_name, nameLen); - GS_FREE(pgxcNodeName); - if (ret == 0) { - return true; - } - } else { - /* deal with the standby dn instance branch - * The pgxc_node_name of the primary and standby instances are the same, So get it by primary DN instance - * get the master instance first - * nodeIndex -> primary DN node index - * instanceidx -> primary DN instance index - * dataPath -> primary DN data path - */ - uint32 nodeIndex = 0; - uint32 instanceidx = 0; - char dataPath[MAXPGPATH] = {0}; - size_t dataPathLen = 0; - - ret = memset_s(dataPath, MAXPGPATH, '\0', MAXPGPATH); - securec_check_c(ret, "\0", "\0"); - - /* - * Each data ring must have a primary instance. So get the node index and data path. - */ - for (uint32 dnId = 0; dnId < g_dn_replication_num - 1; dnId++) { - if (0 == g_node[nodeidx].datanode[i].peerDatanodes[dnId].datanodePeerRole) { - nodeIndex = get_nodeidx_by_HA(g_node[nodeidx].datanode[i].peerDatanodes[dnId].datanodePeerHAIP[0], - g_node[nodeidx].datanode[i].peerDatanodes[dnId].datanodePeerHAPort); - ret = snprintf_s(dataPath, - MAXPGPATH, - MAXPGPATH - 1, - "%s", - g_node[nodeidx].datanode[i].peerDatanodes[dnId].datanodePeerDataPath); - securec_check_ss_c(ret, "\0", "\0"); - break; - } - } - - /* - * Check the result to ensure that the nodeIndex and instance data path of primary instance is found. - */ - if (dataPath[0] == '\0') { - fprintf(stderr, _("ERROR: Failed to get primary DN instance information.\n")); - exit(1); - } - - /* - * get the primary DN instance index by node index and data path - */ - for (j = 0; j < g_node[nodeIndex].datanodeCount; j++) { - if (g_node[nodeIndex].datanode[i].datanodeRole == 0) { - dataPathLen = strlen(g_node[nodeIndex].datanode[j].datanodeLocalDataPath); - if (0 == strncmp(g_node[nodeIndex].datanode[j].datanodeLocalDataPath, - dataPath, - dataPathLen > strlen(dataPath) ? dataPathLen : strlen(dataPath))) { - instanceidx = j; - break; - } - } - } - - pgxcNodeName = GetPgxcNodeNameForMasterDnInstance(nodeIndex, instanceidx); - nameLen = strlen(instance_name) > strlen(pgxcNodeName) ? strlen(instance_name) : strlen(pgxcNodeName); - ret = strncmp(pgxcNodeName, instance_name, nameLen); - GS_FREE(pgxcNodeName); - if (0 == ret) { - return true; - } - } - } - return false; -} - -/* - ****************************************************************************** - Function : validate_instance_name_for_DN - Description : validate the DN instance name. - Input : nodeidx - node id index - instance_name - instance name - Return : bool - ****************************************************************************** -*/ -bool validate_instance_name_for_DN(int32 nodeidx, const char* instance_name) -{ - bool isCorrect = false; - uint32 i = 0; - uint32 instance_id = 0; - char temp_instance_name[MAXPGPATH]; - int rc = 0; - - rc = memset_s(temp_instance_name, MAXPGPATH, '\0', MAXPGPATH); - securec_check_c(rc, "\0", "\0"); - - if ((int)strlen(instance_name) < DN_INSTANCE_LEN) { - return false; - } - - /*single primary multil standby */ - if (g_multi_az_cluster) { - isCorrect = CheckInstanceNameForSinglePrimaryMutilStandby(nodeidx, instance_name); - } else { - /* master_standby */ - if ((int)strlen(instance_name) != DN_INSTANCE_LEN) - return false; - - for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { - if (g_node[nodeidx].datanode[i].datanodeRole == 0) { - if (is_instance_level_correct(g_node[nodeidx].datanode[i].datanodePeerDataPath, - g_node[nodeidx].datanode[i].datanodePeerHAIP[0], - g_node[nodeidx].datanode[i].datanodePeerHAPort, - 1)) - instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeerDataPath, - g_node[nodeidx].datanode[i].datanodePeerHAIP[0], - g_node[nodeidx].datanode[i].datanodePeerHAPort); - else - instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeer2DataPath, - g_node[nodeidx].datanode[i].datanodePeer2HAIP[0], - g_node[nodeidx].datanode[i].datanodePeer2HAPort); - rc = snprintf_s(temp_instance_name, - MAXPGPATH, - MAXPGPATH - 1, - "dn_%d_%d", - (int)g_node[nodeidx].datanode[i].datanodeId, - (int)instance_id); - } else { - if (is_instance_level_correct(g_node[nodeidx].datanode[i].datanodePeerDataPath, - g_node[nodeidx].datanode[i].datanodePeerHAIP[0], - g_node[nodeidx].datanode[i].datanodePeerHAPort, - 0)) - instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeerDataPath, - g_node[nodeidx].datanode[i].datanodePeerHAIP[0], - g_node[nodeidx].datanode[i].datanodePeerHAPort); - else - instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeer2DataPath, - g_node[nodeidx].datanode[i].datanodePeer2HAIP[0], - g_node[nodeidx].datanode[i].datanodePeer2HAPort); - rc = snprintf_s(temp_instance_name, - MAXPGPATH, - MAXPGPATH - 1, - "dn_%d_%d", - (int)instance_id, - (int)g_node[nodeidx].datanode[i].datanodeId); - } - securec_check_ss_c(rc, "\0", "\0"); - - if (strncmp(temp_instance_name, instance_name, strlen(instance_name)) == 0) { - isCorrect = true; - break; - } - - rc = memset_s(temp_instance_name, MAXPGPATH, '\0', MAXPGPATH); - securec_check_c(rc, "\0", "\0"); - } - } - - return isCorrect; -} - -/* - ****************************************************************************** - Function : validate_remote_instance_name - Description : validate remote instance name. - The gs_guc commands like this: "-I instance_name -N nodename". - The instance name type like this: - INSTANCE_COORDINATOR -> cn_instanceId - INSTANCE_GTM -> one - INSTANCE_DATANODE -> dn_masterId_slaveId, dn_masterId_dummyslaveId - Input : nodename - node name - type - instance type - instance_name - instance name - Return : int - ****************************************************************************** -*/ -int validate_remote_instance_name(char* nodename, int type, char* instance_name) -{ - int32 nodeidx = 0; - char temp_instance_name[MAXPGPATH]; - int rc = 0; - bool isCorrect = false; - - rc = memset_s(temp_instance_name, MAXPGPATH, '\0', MAXPGPATH); - securec_check_c(rc, "\0", "\0"); - - nodeidx = get_nodeidx_by_name(nodename); - /* check the node name, makesure it is in cluster_static_config */ - if (nodeidx < 0) { - write_stderr("ERROR: Node %s is not found in static config file.\n", nodename); - return 1; - } - - /* - * INSTANCE_COORDINATOR -> cn_instanceId - * INSTANCE_GTM -> one - * INSTANCE_DATANODE -> dn_masterId_slaveId, dn_masterId_dummyslaveId - */ - if (type == INSTANCE_COORDINATOR) { - rc = snprintf_s(temp_instance_name, MAXPGPATH, MAXPGPATH - 1, "cn_%d", (int)g_node[nodeidx].coordinateId); - securec_check_ss_c(rc, "\0", "\0"); - - if ((CN_INSTANCE_LEN == (int)strlen(instance_name)) && - (0 == strncmp(temp_instance_name, instance_name, strlen(instance_name)))) - isCorrect = true; - } else if (type == INSTANCE_GTM) { - if ((0 != g_node[nodeidx].gtmId) && (GTM_INSTANCE_LEN == (int)strlen(instance_name)) && - (0 == strncmp(instance_name, "one", strlen("one")))) - isCorrect = true; - } else { - isCorrect = validate_instance_name_for_DN(nodeidx, instance_name); - } - - if (isCorrect) { - return 0; - } else { - write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); - return 1; - } -} -/* - ****************************************************************************** - Function : validate_nodename - Description : validate the node name. If the node name is not all, makesure it is - in the cluster static config file. - Input : nodename - node name - Return : int - ****************************************************************************** -*/ -int validate_nodename(char* nodename) -{ - int32 nodeidx = 0; - /* makesure the node name is correct */ - if ((NULL != nodename) && (0 != strncmp(nodename, "all", sizeof("all")))) { - nodeidx = get_nodeidx_by_name(nodename); - /* check the node name, makesure it is in cluster_static_config */ - if (nodeidx < 0) { - write_stderr("ERROR: Node %s is not found in static config file.\n", nodename); - return 1; - } - } - return 0; -} -/* - ****************************************************************************** - Function : check_instance_name - Description : check instance name. We known that the node name and instance name are both not 'NULL' and not 'all'. - Input : nodename - node name - type - instance type - instance_name - instance name - Return : int - ****************************************************************************** -*/ -int check_instance_name(char* nodename, int type, char* instance_name) -{ - char temp_datadir[MAXPGPATH]; - int rc = 0; - - rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); - securec_check_c(rc, "\0", "\0"); - - /* -N nodename -I instance_name*/ - if ((0 != strncmp(nodename, "all", sizeof("all"))) && (0 != strncmp(instance_name, "all", sizeof("all")))) { - if (is_local_node(nodename)) { - if (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_ERROR) { - write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); - return 1; - } - } else { - if (0 != validate_remote_instance_name(nodename, type, instance_name)) - return 1; - } - } - - return 0; -} -/* - ****************************************************************************** - Function : validate_node_instance_name - Description : validate the node name and instance name - Input : nodename - node name - type - instance type - instance_name - instance name - Output : None - Return : int - ****************************************************************************** -*/ -int validate_node_instance_name(char* nodename, int type, char* instance_name) -{ - char temp_datadir[MAXPGPATH]; - int rc = 0; - - rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); - securec_check_c(rc, "\0", "\0"); - - /* Verify that the node name is correct */ - if (0 != validate_nodename(nodename)) - return 1; - - if ((NULL == nodename) && (NULL != instance_name)) { - /* -I instance_name*/ - if ((0 != strncmp(instance_name, "all", sizeof("all"))) && - (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_ERROR)) { - write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); - return 1; - } - } - - if ((NULL != nodename) && (NULL != instance_name)) { - /* skip check '-N all -I all', '-N nodename -I all'*/ - /* command ' -N all -I instance_name' is incorrect expect for DN*/ - if (type != INSTANCE_DATANODE) { - if ((strncmp(nodename, "all", sizeof("all")) == 0) && (strncmp(instance_name, "all", sizeof("all")) != 0)) { - write_stderr( - "ERROR: Instance name %s is incorrect. When -N is 'all', -I must be the same.\n", instance_name); - return 1; - } - } - - /* -N nodename -I instance_name*/ - if (0 != check_instance_name(nodename, type, instance_name)) - return 1; - } - - return 0; -} -/* - ****************************************************************************** - Function : validate_cluster_guc_options - Description : check the -N, -I and -D parameter - Input : nodename - node name - type - node type - instance_name - instance name - indatadir - instance data directory - Output : None - Return : int - ****************************************************************************** -*/ -int validate_cluster_guc_options(char* nodename, int type, char* instance_name, char* indatadir) -{ - if ((NULL != nodename) || (NULL != instance_name)) { - if (0 != init_gauss_cluster_config()) { - (void)write_stderr("ERROR: Failed to get cluster information from static configuration file.\n"); - return 1; - } - } - - if ((NULL == instance_name) && (NULL == indatadir)) { - if (type == INSTANCE_CMAGENT || type == INSTANCE_CMSERVER) { - write_stderr("ERROR: -I all are mandatory for executing gs_guc.\n"); - } else { - write_stderr("ERROR: -D or -I are mandatory for executing gs_guc.\n"); - } - return 1; - } else if ((NULL != instance_name) && (NULL != indatadir)) { - write_stderr("ERROR: -D or -I only need one for executing gs_guc.\n"); - return 1; - } - - if (node_type_number == LARGE_INSTANCE_NUM && (NULL != instance_name) && - (0 != strncmp(instance_name, "all", sizeof("all")))) { - write_stderr("ERROR: when -Z is coordinator and datanode, the -I must be 'all'.\n"); - return 1; - } - - /* The user guarantees the correctness of the -D parameter value*/ - if (0 != validate_node_instance_name(nodename, type, instance_name)) - return 1; - - do_checkvalidate(type); - - return 0; -} - -/* - ****************************************************************************** - Function : save_expect_instance_info - Description : save expect instance information into global parameter. - node name and instance guc configure file - Input : datadir (the directory about instance) - Output : "expected instance path: %s\n", gucconf_file - Return : void - ****************************************************************************** -*/ -void save_expect_instance_info(const char* datadir) -{ - int i = 0; - if (NULL == datadir || '\0' == datadir[0]) { - (void)write_stderr("instance data directory is NULL.\n"); - return; - } - - // Get the configuration file, such as pg_hba.conf/postgresql.conf/cmagent.conf - get_instance_configfile(datadir); - if (CHECK_CONF_COMMAND == ctl_command) { - for (i = 0; i < config_param_number; i++) { - g_expect_gucInfo->nodename_array[g_expect_gucInfo->nodename_num++] = xstrdup(g_local_node_name); - g_expect_gucInfo->gucinfo_array[g_expect_gucInfo->gucinfo_num++] = xstrdup(gucconf_file); - g_expect_gucInfo->paramname_array[g_expect_gucInfo->paramname_num++] = xstrdup(config_param[i]); - g_expect_gucInfo->paramvalue_array[g_expect_gucInfo->paramvalue_num++] = xstrdup("NULL"); - (void)write_stderr( - "expected guc information: %s: %s=NULL: [%s]\n", g_local_node_name, config_param[i], gucconf_file); - } - } else { - g_expect_gucInfo->nodename_array[g_expect_gucInfo->nodename_num++] = xstrdup(g_local_node_name); - g_expect_gucInfo->gucinfo_array[g_expect_gucInfo->gucinfo_num++] = xstrdup(gucconf_file); - (void)write_stderr("expected instance path: [%s]\n", gucconf_file); - } -} - -void check_env_value(const char* input_env_value) -{ - const char* danger_character_list[] = {"|", - ";", - "&", - "$", - "<", - ">", - "`", - "\\", - "'", - "\"", - "{", - "}", - "(", - ")", - "[", - "]", - "~", - "*", - "?", - "!", - "\n", - NULL}; - int i = 0; - - for (i = 0; danger_character_list[i] != NULL; i++) { - if (strstr(input_env_value, danger_character_list[i]) != NULL) { - fprintf(stderr, - _("ERROR: Failed to check environment value: invalid token \"%s\".\n"), - danger_character_list[i]); - exit(1); - } - } -} - -/* - ****************************************************************************** - Function : get_env_value - Description : get environment variable value. - Input : env_var (environment variable name) ,output_env_value - Output : - Return : bool - ****************************************************************************** -*/ -bool get_env_value(const char* env_var, char* output_env_value, size_t env_var_value_len) -{ - char* env_value = NULL; - errno_t rc = 0; - - if (NULL == env_var) - return false; - - env_value = getenv(env_var); - if ((NULL == env_value) || ('\0' == env_value[0])) { - write_stderr( - "ERROR: Failed to obtain environment variable \"%s\". Please check and makesure it is set.\n", env_var); - return false; - } - - if (env_var_value_len <= strlen(env_value)) { - write_stderr("ERROR: The value of environment variable \"%s\" is too long.\n", env_var); - return false; - } - - rc = strcpy_s(output_env_value, env_var_value_len, env_value); - securec_check_c(rc, "\0", "\0"); - return true; -} - -/* - ****************************************************************************** - Function : process_cluster_guc_option - Description : - Input : nodename - - type - - instance_name - - indatadir - - Output : None - Return : void - ****************************************************************************** -*/ -void process_cluster_guc_option(char* nodename, int type, char* instance_name, char* indatadir) -{ - uint32 idx = 0; - int instance_nums = 0; - int nRet = 0; - char local_name[MAX_HOST_NAME_LENGTH]; - int malloc_num = 1; - char *cmpath = NULL; - /* init g_remote_connection_signal */ - g_remote_connection_signal = true; - /* init g_remote_command_result */ - g_remote_command_result = 0; - - g_real_gucInfo = (gucInfo*)pg_malloc(sizeof(gucInfo)); - g_expect_gucInfo = (gucInfo*)pg_malloc(sizeof(gucInfo)); - /* only execute gs_guc in one node and one instance. Only specify the -D parameter */ - if (NULL != indatadir && NULL == nodename) { - nRet = memset_s(local_name, MAX_HOST_NAME_LENGTH, '\0', MAX_HOST_NAME_LENGTH); - securec_check_c(nRet, "\0", "\0"); - if (get_hostname_or_ip(local_name, MAX_HOST_NAME_LENGTH) == false) { - exit(1); - } - - g_local_node_name = xstrdup(local_name); - - /* get current cluster information from cluster_staic_config */ - if (has_static_config() && 0 == init_gauss_cluster_config()) { - for (idx = 0; idx < get_num_nodes(); idx++) { - if (is_local_nodeid(g_node[idx].node)) { - g_local_node_idx = idx; - } - } - } - - malloc_num = 1; - } else { - /* get current cluster information from cluster_staic_config */ - if (0 != init_gauss_cluster_config()) - return; - - /* get local node idx and node name */ - for (idx = 0; idx < get_num_nodes(); idx++) { - if (is_local_nodeid(g_node[idx].node)) { - g_local_node_idx = idx; - } - } - g_local_node_name = getnodename(g_local_node_idx); - - /* On node, coordinator/gtm number <= 1, datanode number >= 0. */ - if (node_type_number == LARGE_INSTANCE_NUM) { - instance_nums = get_all_cndn_num(); - } else if (type == INSTANCE_DATANODE) { - instance_nums = get_all_datanode_num(); - } else if (type == INSTANCE_COORDINATOR) { - instance_nums = get_all_coordinator_num(); - } else if (type == INSTANCE_CMSERVER) { - instance_nums = get_all_cmserver_num(); - } else if (type == INSTANCE_CMAGENT) { - instance_nums = get_all_cmagent_num(); - } else { - instance_nums = get_all_gtm_num(); - } - - g_incorrect_nodeInfo = (nodeInfo*)pg_malloc(sizeof(nodeInfo)); - g_incorrect_nodeInfo->nodename_array = (char**)pg_malloc_zero(get_num_nodes() * sizeof(char*)); - g_incorrect_nodeInfo->num = 0; - - malloc_num = instance_nums + 1; - } - - if (NULL == g_local_node_name || '\0' == g_local_node_name[0]) { - (void)write_stderr("ERROR: Failed to obtain local host name.\n"); - exit(1); - } - - /* init global parameter */ - if (CHECK_CONF_COMMAND == ctl_command || type == INSTANCE_CMSERVER || type == INSTANCE_CMAGENT) { - malloc_num = malloc_num * config_param_number; - } - g_real_gucInfo->nodename_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_real_gucInfo->gucinfo_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_expect_gucInfo->nodename_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_expect_gucInfo->gucinfo_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_real_gucInfo->nodename_num = 0; - g_real_gucInfo->gucinfo_num = 0; - g_expect_gucInfo->nodename_num = 0; - g_expect_gucInfo->gucinfo_num = 0; - - if (CHECK_CONF_COMMAND == ctl_command) { - g_real_gucInfo->paramname_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_real_gucInfo->paramvalue_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_expect_gucInfo->paramname_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_expect_gucInfo->paramvalue_array = (char**)pg_malloc_zero(sizeof(char*) * malloc_num); - g_real_gucInfo->paramname_num = 0; - g_real_gucInfo->paramvalue_num = 0; - g_expect_gucInfo->paramname_num = 0; - g_expect_gucInfo->paramvalue_num = 0; - } - /* CN & DN & GTM && CMA && CMS */ - /* when nodename=NULL, it means only do setting for local node */ - if (NULL == nodename) - { - if ((INSTANCE_CMSERVER == type) || (INSTANCE_CMAGENT == type)) { - cmpath = get_cm_real_path(type); - do_local_instance(type, instance_name, cmpath); - GS_FREE(cmpath); - } else { - do_local_instance(type, instance_name, indatadir); - } - } - else - { - if (0 == strncmp(nodename, "all", sizeof("all"))) - do_all_nodes_instance(instance_name, indatadir); - else - do_remote_instance(nodename, instance_name, indatadir); - } -} - -/* - * the ssh return value: - * 0 : The connection is successful, the command was successful - * 1 : The connection is successful, the command fails - * 127 : The connection is successful, the command fails - * 255 : Connection failed - */ -void -printExecErrorMesg(const char* fcmd, const char *nodename) -{ - if (g_remote_command_result == 127) { - write_stderr(_("ERROR: Failed to execute gs_guc command: %s on node \"%s\", error code is %u, " - "please ensure that gs_guc exists.\n"), fcmd, nodename, g_remote_command_result); - } else if (g_remote_command_result == 255) { - write_stderr(_("ERROR: Failed to execute gs_guc command: %s on node \"%s\", error code is %u, " - "Failed to connect node \"%s\".\n"), fcmd, nodename, g_remote_command_result, nodename); - } else if (g_remote_command_result != 0) { - write_stderr(_("ERROR: Failed to execute gs_guc command: %s on node \"%s\", error code is %u, " - "please get more details from current node path \"$GAUSSLOG/bin/gs_guc\".\n"), - fcmd, nodename, g_remote_command_result); - } -} - -/* - ****************************************************************************** - Function : is_changed_default_value_failed - Description : Modify the default value for parameter "log_directory" and "audit_directory". - Input :type instance type - datadir instance data directory - param_name_str parameter name - index parameter name index - gausslog defaul value - return :true failed to change the default values - false Successfully set default values - ****************************************************************************** -*/ -bool is_changed_default_value_failed(int type, char* datadir, char* param_name_str, int index, const char* gausslog) -{ - char local_inst_name[MAX_INSTANCENAME_LEN] = {0}; - char log_dir[MAX_VALUE_LEN] = {0}; - int32 retval; - int nRet = 0; - - /* get local instance name by data path */ - retval = get_local_instancename_by_dbpath(datadir, local_inst_name); - if (retval == CLUSTER_CONFIG_ERROR) { - (void)write_stderr("ERROR: Failed to obtain instance name by data directory \"%s\".\n", datadir); - return true; - } - - if (type == INSTANCE_COORDINATOR || type == INSTANCE_DATANODE) { - if (0 == strncmp(param_name_str, "log_directory", strlen("log_directory"))) - nRet = snprintf_s(log_dir, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "'%s/pg_log/%s'", gausslog, local_inst_name); - else - nRet = snprintf_s(log_dir, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "'%s/pg_audit/%s'", gausslog, local_inst_name); - securec_check_ss_c(nRet, "\0", "\0"); - } else { - if (0 == strncmp(param_name_str, "log_directory", strlen("log_directory"))) { - nRet = snprintf_s(log_dir, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "'%s/pg_log/gtm'", gausslog); - securec_check_ss_c(nRet, "\0", "\0"); - } else { - (void)write_stderr("ERROR: The parameter \"%s\" don't support gtm instance.\n", param_name_str); - return true; - } - } - - config_value[index] = xstrdup(log_dir); - is_disable_log_directory = true; - - return false; -} - -/* - ****************************************************************************** - Function : check_AZ_value - Description : check the input azName. - Input :AZValue az name - return :true input az name is correct - false input az name is incorrect - ****************************************************************************** -*/ -bool check_AZ_value(const char* AZValue) -{ - // Cause AZName can define by user, the check standard should ensure by om module. Here is a simple check - if (strlen(AZValue) > (CM_AZ_NAME - 1)) { - return false; - } - - return true; -} - -/* - ****************************************************************************** - Function : parse_AZ_result - Description : parse AZ string into the node name list . - Input :AZValue az name - return :NULL input az name is incorrect - other the real result - ****************************************************************************** -*/ -char* parse_AZ_result(char* AZStr, const char* data_dir) -{ - int nRet = 0; - char* vptr = NULL; - char* vouter_ptr = NULL; - char* p = NULL; - char delims[] = ","; - char tmp[MAX_VALUE_LEN] = {0}; - int i = 0; - char azList[3][MAX_INSTANCENAME_LEN] = {0}; - char tmpAzName[MAX_INSTANCENAME_LEN] = {0}; - char** array = NULL; - char* buffer = NULL; - int curlen = 0; - char* azName = NULL; - size_t len = 0; - int ind = -1; - const int az1_index = 0; - const int az2_index = 1; - const int az3_index = 2; - int resultStatus = 0; - - // init tmp az string, array which storage az string - nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = snprintf_s(tmp, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", AZStr); - securec_check_ss_c(nRet, "\0", "\0"); - for (i = 0; i < 3; i++) { - nRet = memset_s(azList[i], MAX_INSTANCENAME_LEN, '\0', MAX_INSTANCENAME_LEN); - securec_check_c(nRet, "\0", "\0"); - } - - // split the az name by ',' - vptr = strtok_r(tmp, delims, &vouter_ptr); - while (NULL != vptr) { - p = vptr; - - // p like this: AZ1, AZ2... - while (isspace((unsigned char)*p)) - p++; - - // Skip if the object already exists, otherwise store it. - size_t azNameLength = strlen(p); - if (check_AZ_value(p)) { - // Skip if the object already exists, otherwise store it - if (azList[az1_index][0] == '\0') { - nRet = strncpy_s(azList[az1_index], MAX_INSTANCENAME_LEN, p, azNameLength); - securec_check_c(nRet, "\0", "\0"); - } else if (azList[az2_index][0] == '\0') { - if (azNameLength != strlen(azList[az1_index]) || - strncmp(p, azList[az1_index], strlen(azList[az1_index])) != 0) { - nRet = strncpy_s(azList[az2_index], MAX_INSTANCENAME_LEN, p, azNameLength); - securec_check_c(nRet, "\0", "\0"); - } - } else if (azList[az3_index][0] == '\0') { - if ((azNameLength != strlen(azList[az1_index]) || - strncmp(p, azList[az1_index], strlen(azList[az1_index])) != 0) && - (azNameLength != strlen(azList[az2_index]) || - strncmp(p, azList[az2_index], strlen(azList[az2_index])) != 0)) { - nRet = strncpy_s(azList[az3_index], MAX_INSTANCENAME_LEN, p, azNameLength); - securec_check_c(nRet, "\0", "\0"); - } - } else { - // nothing to do - } - - // do spilt again - vptr = strtok_r(NULL, delims, &vouter_ptr); - } else { - // input az name is incorrect - (void)write_stderr("Notice: azName value check failed.\n"); - return NULL; - } - } - - // there is no AZ name, this branch can not be reached - if ('\0' == azList[0][0]) { - // input az name is incorrect - return NULL; - } - - // sort AZ list - azName = get_AZname_by_nodename(g_local_node_name); - if (NULL == azName) { - (void)write_stderr("ERROR: Failed to obtain AZ name by local node.\n"); - return NULL; - } - - for (i = 0; i < 3; i++) { - if (0 == strncmp(azList[i], azName, strlen(azList[i]) > strlen(azName) ? strlen(azList[i]) : strlen(azName))) { - ind = i; - } - } - - if (ind > 0) { - // swap azlist[0] and azlist[ind] - // save azlist[0] - nRet = memset_s(tmpAzName, MAX_INSTANCENAME_LEN, '\0', MAX_INSTANCENAME_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = strncpy_s(tmpAzName, MAX_INSTANCENAME_LEN, azList[0], strlen(azList[0])); - securec_check_c(nRet, "\0", "\0"); - // set azlist[0] to azlist[ind] - nRet = memset_s(azList[0], MAX_INSTANCENAME_LEN, '\0', MAX_INSTANCENAME_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = strncpy_s(azList[0], MAX_INSTANCENAME_LEN, azList[ind], strlen(azList[ind])); - securec_check_c(nRet, "\0", "\0"); - // set azlist[ind] to tmpAzName - nRet = memset_s(azList[ind], MAX_INSTANCENAME_LEN, '\0', MAX_INSTANCENAME_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = strncpy_s(azList[ind], MAX_INSTANCENAME_LEN, tmpAzName, strlen(tmpAzName)); - securec_check_c(nRet, "\0", "\0"); - } - GS_FREE(azName); - - // init array - array = (char**)pg_malloc(3 * sizeof(char*)); - array[0] = NULL; - array[1] = NULL; - array[2] = NULL; - - resultStatus = get_nodename_list_by_AZ(azList[0], data_dir, &array[0]); - PROCESS_STATUS(resultStatus); - // input az name is incorrect - if (NULL == array[0]) { - (void)write_log("ERROR: The AZ name \"%s\" does not be found on cluster. please makesure the AZ string " - "\"%s\" is correct.\n", - azList[0], - AZStr); - goto failed; - } - len += strlen(array[0]) + 1; - - if ('\0' != azList[1][0]) { - resultStatus = get_nodename_list_by_AZ(azList[1], data_dir, &array[1]); - PROCESS_STATUS(resultStatus); - // input az name is incorrect - if (NULL == array[1]) { - (void)write_log("ERROR: The AZ name \"%s\" does not be found on cluster. please makesure the AZ string " - "\"%s\" is correct.\n", - azList[1], - AZStr); - goto failed; - } - len += strlen(array[1]) + 1; - } - - if ('\0' != azList[2][0]) { - resultStatus = get_nodename_list_by_AZ(azList[2], data_dir, &array[2]); - PROCESS_STATUS(resultStatus); - // input az name is incorrect - if (NULL == array[2]) { - (void)write_log("ERROR: The AZ name \"%s\" does not be found on cluster. please makesure the AZ string " - "\"%s\" is correct.\n", - azList[2], - AZStr); - goto failed; - } - len += strlen(array[2]) + 1; - } - - // get the string information - buffer = (char*)pg_malloc_zero((len + 1) * sizeof(char)); - for (i = 0; i < 3; i++) { - if (NULL != array[i] && strlen(array[i]) > 0) { - nRet = snprintf_s(buffer + curlen, (len + 1 - curlen), (len - curlen), "%s,", array[i]); - securec_check_ss_c(nRet, buffer, "\0"); - curlen = curlen + nRet; - } - } - if (strlen(buffer) >= 2) { - // skip the last character ',' - buffer[strlen(buffer) - 1] = '\0'; - } else { - (void)write_stderr( - "ERROR: There is no standby node, please makesure the AZ string \"%s\" is correct.\n", AZStr); - goto failed; - } - - GS_FREE(array[0]); - GS_FREE(array[1]); - GS_FREE(array[2]); - GS_FREE(array); - return buffer; - -failed: - GS_FREE(array[0]); - GS_FREE(array[1]); - GS_FREE(array[2]); - GS_FREE(array); - GS_FREE(buffer); - return NULL; -} - -/* - ****************************************************************************** - Function : get_nodename_number_from_nodelist - Description : get the number of nodenames in the nodename string. String is split with ',' - Input :AZValue namelist - return :int the nodename number - ****************************************************************************** -*/ -int get_nodename_number_from_nodelist(const char* namelist) -{ - char* ptr = NULL; - char* outer_ptr = NULL; - char delims[] = ","; - size_t len = 0; - int count = 0; - char* buffer = NULL; - int nRet = 0; - - len = strlen(namelist) + 1; - buffer = (char*)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(buffer, len, len - 1, "%s", namelist); - securec_check_ss_c(nRet, buffer, "\0"); - - ptr = strtok_r(buffer, delims, &outer_ptr); - while (NULL != ptr) { - count++; - ptr = strtok_r(NULL, delims, &outer_ptr); - } - - GS_FREE(buffer); - return count; -} - -/* - ****************************************************************************** - Function : parse_datanodename_result - Description : check data node name. - Input :datanodenamelist data node name - return :NULL input data node name is incorrect - other the real result - ****************************************************************************** -*/ -char *ParseDatanameResult(const char *datanodeNameList, const char *dataDir) -{ - int nRet; - char *vptr = NULL; - char *vouterPtr = NULL; - char *p = NULL; - char delims[] = ","; - char tmp[MAX_VALUE_LEN] = {0}; - char *buffer = NULL; - size_t len; - - // init tmp nodeName string, array which storage nodeName string - nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = snprintf_s(tmp, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", datanodeNameList); - securec_check_ss_c(nRet, "\0", "\0"); - - // split the node name by ',' - vptr = strtok_r(tmp, delims, &vouterPtr); - while (vptr != NULL) { - p = vptr; - - // p like this: dn_6001, dn_6002 - while (isspace((unsigned char)*p)) { - p++; - } - - if (CheckDataNameValue(p, dataDir)) { - // do split again - vptr = strtok_r(NULL, delims, &vouterPtr); - } else { - // input node name is incorrect - write_stderr("Notice: datanodename value check failed.(datanodename=%s)\n", p); - return NULL; - } - } - - len = strlen(datanodeNameList) + 1; - // get the string information - buffer = (char *)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(buffer, len, (len - 1), "%s", datanodeNameList); - securec_check_ss_c(nRet, buffer, "\0"); - return buffer; -} - -/* - ****************************************************************************** - Function : get_AZ_value - Description : parse AZ string into the node name list . - Input :value the parameter value from input - ****************************************************************************** -*/ -char* get_AZ_value(const char* value, const char* data_dir) -{ - size_t minLen = 0; - int nRet = 0; - char tmp[MAX_VALUE_LEN] = {0}; - char* p = NULL; - char* q = NULL; - char* s = NULL; - char preStr[16] = {0}; - char level[4] = {0}; - int i = 0; - int j = 0; - int count = 0; - char* nodenameList = NULL; - char* result = NULL; - size_t len = 0; - char* az1 = getAZNamebyPriority(g_az_master); - char* vouter_ptr = NULL; - char delims[] = ","; - char* vptr = NULL; - char emptyvalue[] = "''"; - bool isNodeName = false; - - if (az1 != NULL) { - minLen = strlen("ANY X()") + strlen(az1); - } else { - (void)write_stderr("ERROR: can not find AZ_MASTER Name, current az_master priority=%u.\n", g_az_master); - return NULL; - } - - nRet = memset_s(preStr, sizeof(preStr) / sizeof(char), '\0', sizeof(preStr) / sizeof(char)); - securec_check_c(nRet, "\0", "\0"); - nRet = memset_s(level, sizeof(level) / sizeof(char), '\0', sizeof(level) / sizeof(char)); - securec_check_c(nRet, "\0", "\0"); - - /* the value including ''' or space, so skip it */ - nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - i = 0; - j = 1; - while (j < (int)strlen(value) - 1) { - if (!isspace(value[j])) { - tmp[i] = value[j]; - i++; - j++; - } else { - j++; - } - } - - /* check value length */ - if (strlen(value) > MAX_VALUE_LEN) { - (void)write_stderr("ERROR: The value of pamameter synchronous_standby_names is incorrect.\n"); - return NULL; - } - - p = tmp; - if (strlen(p) == 0 || *p == '*') { - len = strlen(emptyvalue) + strlen(p) + 1; - result = (char*)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(result, len, len - 1, "'%s'", p); - securec_check_ss_c(nRet, "\0", "\0"); - return result; - } - - // Assign values to preStr - /* FIRST branch */ - if (0 == strncmp(p, "FIRST", strlen("FIRST"))) { - nRet = strncpy_s(preStr, sizeof(preStr) / sizeof(char), "FIRST ", strlen("FIRST ")); - securec_check_c(nRet, "\0", "\0"); - p = p + strlen("FIRST"); - } - /* ANY branch */ - if (0 == strncmp(p, "ANY", strlen("ANY"))) { - nRet = strncpy_s(preStr, sizeof(preStr) / sizeof(char), "ANY ", strlen("ANY ")); - securec_check_c(nRet, "\0", "\0"); - p = p + strlen("ANY"); - } - - if (strncmp(p, "NODE", strlen("NODE")) == 0) { - isNodeName = true; - p = p + strlen("NODE"); - } - - /* make sure it is digit and between 1 and 7, including 1 and 7 */ - if (isdigit((unsigned char)*p)) { - nRet = snprintf_s(level, sizeof(level) / sizeof(char), - sizeof(level) / sizeof(char) - 1, "%c", (unsigned char)*p); - securec_check_ss_c(nRet, "\0", "\0"); - if (atoi(level) < 1 || atoi(level) > 7) { - goto failed; - } - - if (strchr(p, '(') && strrchr(p, ')')) { - q = strchr(p, '('); - q++; - s = strrchr(p, ')'); - s[0] = '\0'; - } else { - goto failed; - } - } else { - q = p; - } - - /* skip this branch ANY 1() or ANY 1(*) */ - if (*q == '\0' || *q == '*') { - goto failed; - } - - if (isNodeName) { - // parse and check nodeName string - nodenameList = ParseDatanameResult(q, data_dir); - } else { - // parse and check the AZName string - nodenameList = parse_AZ_result(q, data_dir); - } - - if (NULL == nodenameList) { - // try dn - - len = strlen(q) + 1; - s = (char *)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(s, len, len - 1, "%s", q); - securec_check_ss_c(nRet, s, "\0"); - - vptr = strtok_r(s, delims, &vouter_ptr); - while (vptr != NULL) { - p = vptr; - - if (CheckDataNameValue(p, data_dir) == false) { - GS_FREE(s); - goto failed; - } - vptr = strtok_r(NULL, delims, &vouter_ptr); - } - - GS_FREE(s); - nodenameList = (char *)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(nodenameList, len, len - 1, "%s", q); - securec_check_ss_c(nRet, nodenameList, "\0"); - } else if ('\0' == nodenameList[0]) { - (void)write_stderr("ERROR: There is no standby node name. Please make sure the value of " - "synchronous_standby_names is correct.\n"); - GS_FREE(nodenameList); - return NULL; - } - // X must less than node name numbers - count = get_nodename_number_from_nodelist(nodenameList); - if (atoi(level) > count) { - (void)write_stderr("ERROR: The sync number(%d) must less or equals to the number of standby node names(%d). " - "Please make sure the value of synchronous_standby_names is correct.\n", - atoi(level), count); - GS_FREE(nodenameList); - return NULL; - } - - // ANY/FIRST X + nodenameList + () + '' + \0 - if (atoi(level) >= 1) { - len = strlen(preStr) + 6 + strlen(nodenameList); - result = (char*)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(result, len, len - 1, "'%s%s(%s)'", preStr, level, nodenameList); - securec_check_ss_c(nRet, "\0", "\0"); - } else { - len = 3 + strlen(nodenameList); - result = (char*)pg_malloc_zero(len * sizeof(char)); - nRet = snprintf_s(result, len, len - 1, "'%s'", nodenameList); - securec_check_ss_c(nRet, "\0", "\0"); - } - - GS_FREE(nodenameList); - return result; - -failed: - GS_FREE(nodenameList); - GS_FREE(result); - (void)write_stderr("ERROR: The value of pamameter synchronous_standby_names is incorrect.\n"); - return NULL; -} - -/* - ****************************************************************************** - Function : do_local_para_value_change - Description : only support parameter "log_directory" and "audit_directory". - If we want to disable "log_directory", using the default value "$GAUSSLOG/pg_log/instance_name" - If we want to disable "audit_directory", using the default value "$GAUSSLOG/pg_audit/instance_name" - ****************************************************************************** -*/ -int do_local_para_value_change(int type, char* datadir) -{ - bool is_failed = false; - int i = 0; - char gausslog[MAXPGPATH] = {0}; - char staticfile[MAXPGPATH] = {0}; - char gausshome[MAXPGPATH] = {0}; - int nRet = 0; - struct stat statbuf; - - if (type != INSTANCE_COORDINATOR && type != INSTANCE_DATANODE && type != INSTANCE_CMSERVER && - type != INSTANCE_CMAGENT && type != INSTANCE_GTM) { - (void)write_stderr("ERROR: The instance type is incorrect.\n"); - return FAILURE; - } - - for (i = 0; i < config_param_number; i++) { - if (0 == strncmp(config_param[i], - "synchronous_standby_names", - strlen(config_param[i]) > strlen("synchronous_standby_names") - ? strlen(config_param[i]) - : strlen("synchronous_standby_names"))) { - if (type != INSTANCE_DATANODE) { - (void)write_stderr( - "ERROR: The pamameter synchronous_standby_names only can be used for datanode type.\n"); - return FAILURE; - } - - if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) { - g_need_changed = false; - } else { - check_env_value(gausshome); - nRet = snprintf_s(staticfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, STATIC_CONFIG_FILE); - securec_check_ss_c(nRet, "\0", "\0"); - if (lstat(staticfile, &statbuf) != 0) { - g_need_changed = false; - } else { - if (0 != init_gauss_cluster_config()) { - (void)write_stderr( - "ERROR: Failed to get cluster information from static configuration file.\n"); - return FAILURE; - } - } - } - /* init g_local_instance_path */ - if (NULL == g_local_instance_path) { - g_local_instance_path = xstrdup(datadir); - } - } - if (NULL == config_value[i] || is_disable_log_directory) { - if (0 == strncmp(config_param[i], "log_directory", strlen("log_directory")) || - 0 == strncmp(config_param[i], "audit_directory", strlen("audit_directory"))) { - if (!get_env_value("GAUSSLOG", gausslog, sizeof(gausslog) / sizeof(char))) - return FAILURE; - - check_env_value(gausslog); - is_failed = is_changed_default_value_failed(type, datadir, config_param[i], i, gausslog); - } - } - } - - if (is_failed) - return FAILURE; - return SUCCESS; -} - -int do_local_guc_command(int type, char* temp_datadir) -{ - if ('\0' != temp_datadir[0]) { - /* - * When do check, do_local_para_value_change is not be used. - */ - if ((type != INSTANCE_CMAGENT) && (type != INSTANCE_CMSERVER)) { - if ((CHECK_CONF_COMMAND != ctl_command) && (FAILURE == do_local_para_value_change(type, temp_datadir))) - return FAILURE; - } - - if (0 != process_guc_command(temp_datadir)) - return FAILURE; - } - return SUCCESS; -} - -/* - ****************************************************************************** - Function : do_command_in_local_node - Description : set/reload guc parameter in local node - Input : type (instance type) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** -*/ -void do_command_in_local_node(int type, char* indatadir) -{ - char datadir[MAXPGPATH] = {0}; - - /* process only in datadir */ - if (NULL == indatadir) { - char* envvar = NULL; - // datadir = /* get the it from PGDATA */ - if ((INSTANCE_COORDINATOR == type) || (INSTANCE_DATANODE == type)) - envvar = "PGDATA"; - else if (INSTANCE_GTM == type) - envvar = "GTMDATA"; - else - return; - - if (!get_env_value(envvar, datadir, sizeof(datadir) / sizeof(char))) - return; - if (NULL != datadir) { - check_env_value(datadir); - } - /* process the PGDATA / GTMDATA */ - if (checkPath(datadir) != 0) { - write_stderr(_("realpath(%s) failed : %s!\n"), datadir, strerror(errno)); - } - save_expect_instance_info(datadir); - if (FAILURE == do_local_guc_command(type, datadir)) - return; - } else { - /* process the -D option */ - if (checkPath(indatadir) != 0) { - write_stderr(_("realpath(%s) failed : %s!\n"), indatadir, strerror(errno)); - } - save_expect_instance_info(indatadir); - if (FAILURE == do_local_guc_command(type, indatadir)) - return; - } -} - -/* - ****************************************************************************** - Function : do_command_with_all_option - Description : set/reload guc parameter using "-I all" option - Input : type (instance type) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** -*/ -void do_command_with_all_option(int type, char* indatadir) -{ - if (node_type_number == LARGE_INSTANCE_NUM) - do_command_for_cndn(type, indatadir); - else if (type == INSTANCE_COORDINATOR) - do_command_for_cn_gtm(type, indatadir, true); - else if (type == INSTANCE_GTM) - do_command_for_cn_gtm(type, indatadir, false); - else if (type == INSTANCE_DATANODE) - do_command_for_dn(type, indatadir); - else if ((type == INSTANCE_CMAGENT) || (type == INSTANCE_CMSERVER)) - do_command_for_cm(type, indatadir); - else - return; -} - -/* - ****************************************************************************** - Function : do_command_for_cn - Description : - Input : type (instance type) - indatadir (the instance data path) - isCoordinator if true is Coordinator, else is gtm - Output : None - Return : void - ****************************************************************************** -*/ -void do_command_for_cn_gtm(int type, char* indatadir, bool isCoordinator) -{ - char temp_datadir[MAXPGPATH] = {0}; - errno_t rc = 0; - - if (isCoordinator) - rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->DataPath, sizeof(temp_datadir) / sizeof(char)); - else - rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->gtmLocalDataPath, sizeof(temp_datadir) / sizeof(char)); - securec_check_c(rc, "\0", "\0"); - - save_expect_instance_info(temp_datadir); - if (FAILURE == do_local_guc_command(type, temp_datadir)) - return; -} - -/* - ****************************************************************************** - Function : do_command_for_dn - Description : - Input : type (instance type) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** -*/ -void do_command_for_dn(int type, char* indatadir) -{ - char temp_datadir[MAXPGPATH] = {0}; - uint32 i = 0; - errno_t rc = 0; - - for (i = 0; i < get_local_num_datanode(); i++) { - rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->datanode[i].datanodeLocalDataPath, sizeof(temp_datadir) / sizeof(char)); - securec_check_c(rc, "\0", "\0"); - save_expect_instance_info(temp_datadir); - } - - for (i = 0; i < get_local_num_datanode(); i++) { - rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->datanode[i].datanodeLocalDataPath, sizeof(temp_datadir) / sizeof(char)); - securec_check_c(rc, "\0", "\0"); - if (FAILURE == do_local_guc_command(type, temp_datadir)) { - return; - } - } -} - -/* - ****************************************************************************** - Function : do_command_for_cm - Description : - Input : type (instance type) - indatadir (the instance data path) - isCmserver (if true is cmserver and pathname is "cm_server", else is cmagent and pathname is -"cm_agent") Output : None Return : void - ****************************************************************************** -*/ -void -do_command_for_cm(int type, char* indatadir) -{ - char temp_datadir[MAXPGPATH] = {0}; - char cm_dir[MAXPGPATH] = {0}; - int nRet = 0; - errno_t rc = 0; - - rc = memcpy_s(cm_dir, sizeof(cm_dir)/sizeof(char), g_currentNode->cmDataPath, sizeof(cm_dir)/sizeof(char)); - securec_check_c(rc, "\0", "\0"); - - if (cm_dir[0] == '\0') { - write_stderr("Failed to get cm base datapath from static config file."); - return; - } - - if (type == INSTANCE_CMAGENT) { - nRet = snprintf_s(temp_datadir, sizeof(temp_datadir)/sizeof(char), - sizeof(temp_datadir)/sizeof(char) -1, "%s/cm_agent", cm_dir); - securec_check_ss_c(nRet, "\0", "\0"); - } else { - if (g_currentNode->cmServerLevel == 1) { - nRet = snprintf_s(temp_datadir, sizeof(temp_datadir)/sizeof(char), - sizeof(temp_datadir)/sizeof(char) -1, "%s/cm_server", cm_dir); - securec_check_ss_c(nRet, "\0", "\0"); - } else { - /* There is no cmserver instance on the node */ - return; - } - } - - save_expect_instance_info(temp_datadir); - if (FAILURE == do_local_guc_command(type, temp_datadir)) - return; -} - -/* - ****************************************************************************** - Function : do_command_for_datainstance - Description : - Input : type (instance type) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** -*/ -void do_command_for_cndn(int type, char* indatadir) -{ - do_command_for_cn_gtm(INSTANCE_COORDINATOR, indatadir, true); - do_command_for_dn(INSTANCE_DATANODE, indatadir); -} - -/* - ****************************************************************************** - Function : do_command_with_instance_name_option - Description : set/reload guc parameter using "-I instance_name" option - Input : type (instance type) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** -*/ -void do_command_with_instance_name_option(int type, char* instance_name) -{ - if (node_type_number == LARGE_INSTANCE_NUM) { - do_command_with_instance_name_option_local(INSTANCE_COORDINATOR, instance_name); - - do_command_with_instance_name_option_local(INSTANCE_DATANODE, instance_name); - } else { - do_command_with_instance_name_option_local(type, instance_name); - } -} - -char * -get_cm_real_path(int type) -{ - char *cmpath = NULL; - if (INSTANCE_CMSERVER == type) { - if (1 == g_node[g_local_node_idx].cmServerLevel && g_node[g_local_node_idx].cmDataPath[0] != '\0') { - cmpath = xstrdup(g_node[g_local_node_idx].cmDataPath); - } else { - write_stderr("ERROR: Failed to get cmserver instance path.\n"); - exit(1); - } - } else if (INSTANCE_CMAGENT == type) { - if (g_node[g_local_node_idx].cmDataPath[0] != '\0') { - cmpath = xstrdup(g_node[g_local_node_idx].cmDataPath); - } else { - write_stderr("ERROR: Failed to get cmagent instance path.\n"); - exit(1); - } - } else { - write_stderr("ERROR: the instance type is incorrect.\n"); - exit(1); - } - return cmpath; -} - -void do_command_with_instance_name_option_local(int type, char* instance_name) -{ - char temp_datadir[MAXPGPATH]; - int rc = 0; - - rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); - securec_check_c(rc, "\0", "\0"); - - if (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_SUCCESS) { - save_expect_instance_info(temp_datadir); - if (FAILURE == do_local_guc_command(type, temp_datadir)) - return; - } else { - write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); - exit(1); - } -} -/* - ****************************************************************************** - Function : do_local_instance - Description : set/reload guc parameter for local node. - 1. -N and -I are NULL, Only specify -D parameter - 2. -N is NULL, -I is "all", specify -D parameter - 3. -N is NULL, specify -D or -I parameter - Input : type (instance type) - instance_name (instance name) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** -*/ -void -do_local_instance(int type, char* instance_name, char* indatadir) -{ - /* process the command in local node---Only specify -D parameter, -N and -I are NULL */ - if (NULL == instance_name) { - do_command_in_local_node(type, indatadir); - } else if (0 == strncmp(instance_name, "all", sizeof("all"))) { - /* process the -I all option ---specify -D parameter, -I is "all", -N is NULL */ - do_command_with_all_option(type, indatadir); - } else { - /* process the -I instance_name option. This branch CMA && CMS can not be reached */ - do_command_with_instance_name_option(type, instance_name); - } -} -/* - ****************************************************************************** - Function : do_remote_instance - Description : set/reload guc parameter for remote node - Input : nodename (node name) - instance_name (instance name) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** -*/ -void do_remote_instance(char* nodename, const char* instance_name, const char* indatadir) -{ - if (node_type_number == LARGE_INSTANCE_NUM) { - nodetype = INSTANCE_COORDINATOR; - do_remote_instance_local(nodename, instance_name, indatadir); - - nodetype = INSTANCE_DATANODE; - do_remote_instance_local(nodename, instance_name, indatadir); - } else { - do_remote_instance_local(nodename, instance_name, indatadir); - } -} - -void do_remote_instance_local(char* nodename, const char* instance_name, const char* indatadir) -{ - char* command = NULL; - int32 nodeidx; - bool local_mode = !strncmp(g_local_node_name, - nodename, - strlen(g_local_node_name) > strlen(nodename) ? strlen(g_local_node_name) : strlen(nodename)); - command = form_commandline_options(instance_name, indatadir, local_mode); - nodeidx = get_nodeidx_by_name(nodename); - - /* check the node name, makesure it is in cluster_staic_config */ - if (nodeidx < 0) { - write_stderr("ERROR: Node %s not found in static config file\n", nodename); - GS_FREE(command); - exit(1); - } - - (void)execute_guc_command_in_remote_node(nodeidx, command); - - GS_FREE(command); -} - -/* - ****************************************************************************** - Function : do_all_nodes_instance - Description : set/reload guc parameter for all cluster node - Input : instance_name (instance name) - indatadir (the instance data path) - Output : None - Return : void - ****************************************************************************** -*/ -void do_all_nodes_instance(const char* instance_name, const char* indatadir) -{ - if (node_type_number == LARGE_INSTANCE_NUM) { - nodetype = INSTANCE_COORDINATOR; - do_all_nodes_instance_local(instance_name, indatadir); - - nodetype = INSTANCE_DATANODE; - do_all_nodes_instance_local(instance_name, indatadir); - } else { - do_all_nodes_instance_local(instance_name, indatadir); - } -} -/* - ****************************************************************************** - Function : do_all_nodes_instance_local - Description : do_all_nodes_instance_local. When do check in serial, do set/reload in parallel - Input : instance_name, indatadir - Output : void - Return : void - ****************************************************************************** -*/ -void do_all_nodes_instance_local(const char* instance_name, const char* indatadir) -{ - if (CHECK_CONF_COMMAND == ctl_command) { - do_all_nodes_instance_local_in_serial(instance_name, indatadir); - } else { - do_all_nodes_instance_local_in_parallel_loop(instance_name, indatadir); - } -} - -void do_all_nodes_instance_local_in_serial(const char* instance_name, const char* indatadir) -{ - uint32 idx = 0; - for (idx = 0; idx < get_num_nodes(); idx++) { - char* nodename = getnodename(idx); - bool local_mode = !strncmp(g_local_node_name, - nodename, - strlen(g_local_node_name) > strlen(nodename) ? strlen(g_local_node_name) : strlen(nodename)); - char* command = form_commandline_options(instance_name, indatadir, local_mode); - (void)execute_guc_command_in_remote_node(idx, command); - GS_FREE(command); - } -} - -static void init_global_command() -{ - int i; - int rc = 0; - PARALLEL_COMMAND_S* curr_cxt = NULL; - - g_max_commands_parallel = get_num_nodes(); - g_parallel_command_cxt = (PARALLEL_COMMAND_S*)pg_malloc(g_max_commands_parallel * sizeof(PARALLEL_COMMAND_S)); - - rc = memset_s(g_parallel_command_cxt, - g_max_commands_parallel * sizeof(PARALLEL_COMMAND_S), - '\0', - g_max_commands_parallel * sizeof(PARALLEL_COMMAND_S)); - securec_check_c(rc, "\0", "\0"); - - g_cur_commands_parallel = 0; - for (i = 0; i < g_max_commands_parallel; i++) { - curr_cxt = &g_parallel_command_cxt[i]; - curr_cxt->cur_buf_loc = 0; - rc = memset_s(curr_cxt->readbuf, sizeof(curr_cxt->readbuf), '\0', sizeof(curr_cxt->readbuf)); - securec_check_c(rc, "\0", "\0"); - - curr_cxt->pfp = NULL; - curr_cxt->nodename = xstrdup(getnodename((uint32)i)); - } -} - -static void reset_global_command() -{ - int i; - for (i = 0; i < (int)g_incorrect_nodeInfo->num; i++) { - GS_FREE(g_incorrect_nodeInfo->nodename_array[i]); - } - g_incorrect_nodeInfo->num = 0; - g_cur_commands_parallel = 0; -} -static void do_all_nodes_instance_local_in_parallel_loop(const char* instance_name, const char* indatadir) -{ - int i; - init_global_command(); - write_stderr("Begin to perform the total nodes: %d.\n", g_max_commands_parallel); - for (i = 0; i < LOOP_COUNT; i++) { - do_all_nodes_instance_local_in_parallel(instance_name, indatadir); - if (g_incorrect_nodeInfo->num == 0 || i == LOOP_COUNT - 1) { - break; - } - - write_stderr("Retry to perform the failed nodes: %d.\n", (int32)g_incorrect_nodeInfo->num); - reset_global_command(); - (void)SleepInMilliSec(100); - } - - for (i = 0; i < g_max_commands_parallel; i++) { - GS_FREE(g_parallel_command_cxt[i].nodename); - } - GS_FREE(g_parallel_command_cxt); - - if (g_incorrect_nodeInfo->num == 0) { - (void)write_stderr("ALL: Success to perform gs_guc!\n\n"); - } - else { - (void)write_stderr("ALL: Failure to perform gs_guc!\n\n"); - exit(1); - } -} - -static bool needPassNode(const char* nodename) -{ - int cmpLen = 0; - char* ignoreNode = NULL; - - for (uint32 i = 0; i < g_ignore_nodeInfo->num; i++) { - ignoreNode = g_ignore_nodeInfo->nodename_array[i]; - cmpLen = (strlen(ignoreNode) > strlen(nodename)) ? strlen(ignoreNode) : strlen(nodename); - if (strncmp(ignoreNode, nodename, cmpLen) == 0) { - return true; - } - } - return false; -} - -void do_all_nodes_instance_local_in_parallel(const char* instance_name, const char* indatadir) -{ - int idx = 0; - char* nodename = NULL; - int buf_len = 0; - bool if_for_all_instance = true; - int open_count = 0; - bool is_local_node = false; - char* command_local = form_commandline_options(instance_name, indatadir, true); - char* command_remote = form_commandline_options(instance_name, indatadir, false); - - if ((instance_name != NULL) && (strncmp(instance_name, "all", sizeof("all")) != 0)) { - if_for_all_instance = false; - } - - for (idx = 0; idx < g_max_commands_parallel; idx++) { - if (if_for_all_instance == false && validate_instance_name_for_DN(idx, instance_name) == false) { - continue; - } - /* - * When instance type is INSTANCE_CMSERVER, only the nodes that contain the cm_server instance are setting. - * When instance type is INSTANCE_GTM/INSTANCE_COORDINATOR/INSTANCE_DATANODE, only the nodes that contain the gtm instance are setting. - */ - if ((nodetype == INSTANCE_CMSERVER && 1 != g_node[idx].cmServerLevel) || - (nodetype == INSTANCE_GTM && 1 != g_node[idx].gtm) || - (nodetype == INSTANCE_COORDINATOR && 1 != g_node[idx].coordinate) || - (nodetype == INSTANCE_DATANODE && 0 == g_node[idx].datanodeCount)) { - continue; - } - if (NULL == g_parallel_command_cxt[idx].nodename) { - continue; - } - - nodename = g_parallel_command_cxt[idx].nodename; - if ((g_ignore_nodeInfo != NULL) && needPassNode(nodename)) { - continue; - } - open_count++; - - buf_len = (strlen(g_local_node_name) > strlen(nodename)) ? strlen(g_local_node_name) : strlen(nodename); - is_local_node = (0 == strncmp(g_local_node_name, nodename, buf_len)) ? true : false; - is_local_node ? executePopenCommandsParallel(command_local, idx, is_local_node) : - executePopenCommandsParallel(command_remote, idx, is_local_node); - } - write_stderr("Popen count is %d, Popen success count is %d, Popen failure count is %d.\n", - open_count, g_cur_commands_parallel, (int)g_incorrect_nodeInfo->num); - - readPopenOutputParallel(command_local, if_for_all_instance); - - GS_FREE(command_local); - GS_FREE(command_remote); -} - -/* - * Execute commands in parallel - */ -static void executePopenCommandsParallel(const char* cmd, int idx, bool is_local_node) -{ - int rc = 0; - PARALLEL_COMMAND_S* curr_cxt = NULL; - char* fcmd = NULL; - char* mpprvFile = NULL; - int nRet = 0; - size_t len_fcmd = 0; - char* nodename = NULL; - /* the temp directory that storage gs_guc result information */ - char gausshome[MAXPGPATH] = {0}; - - curr_cxt = &g_parallel_command_cxt[idx]; - nodename = g_parallel_command_cxt[idx].nodename; - - curr_cxt->cur_buf_loc = 0; - rc = memset_s(curr_cxt->readbuf, sizeof(curr_cxt->readbuf), '\0', sizeof(curr_cxt->readbuf)); - securec_check_c(rc, "\0", "\0"); - len_fcmd = strlen(cmd) + strlen(nodename) + NAMEDATALEN + MAXPGPATH; - - if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) { - return; - } - check_env_value(gausshome); - - mpprvFile = GetEnvStr("MPPDB_ENV_SEPARATE_PATH"); - /* execute gs_guc commands by 'ssh' */ - if (mpprvFile == NULL) { - fcmd = (char*)pg_malloc_zero(len_fcmd); - nRet = is_local_node ? snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "%s 2>&1", cmd) : - snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "pssh -s -H %s \"%s\" 2>&1", nodename, cmd); - } else { - if (MAXPGPATH <= strlen(mpprvFile)) { - write_stderr("ERROR: The value of environment variable \"MPPDB_ENV_SEPARATE_PATH\" is too long."); - GS_FREE(mpprvFile); - return; - } - check_env_value(mpprvFile); - len_fcmd = len_fcmd + (int)strlen(mpprvFile); - fcmd = (char*)pg_malloc_zero(len_fcmd); - nRet = is_local_node ? snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "source %s; %s 2>&1", mpprvFile, cmd) : - snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "pssh -s -H %s \"source %s; %s\" 2>&1", nodename, mpprvFile, cmd); - } - securec_check_ss_c(nRet, fcmd, "\0"); - - curr_cxt->pfp = popen(fcmd, "r"); - GS_FREE(fcmd); - GS_FREE(mpprvFile); - - if (NULL != curr_cxt->pfp) { - g_cur_commands_parallel++; - uint32 flags; - int fd = fileno(curr_cxt->pfp); - flags = fcntl(fd, F_GETFL, 0); - flags |= O_NONBLOCK; - (void)fcntl(fd, F_SETFL, flags); - } - else { - g_incorrect_nodeInfo->nodename_array[g_incorrect_nodeInfo->num++] = xstrdup(curr_cxt->nodename); - } - return; -} - -/* - * read popen output parallel - */ -static void readPopenOutputParallel(const char* cmd, bool if_for_all_instance) -{ - int rc = 0; - int idx = 0; - bool read_pending = true; - char* result = NULL; - PARALLEL_COMMAND_S* curr_cxt = g_parallel_command_cxt; - int i = 0; - char* endsp = NULL; - int successNumber = 0; - int failedNumber = 0; - int instance_nums = 0; - uint32 ret = 0; - - if (nodetype == INSTANCE_COORDINATOR) { - instance_nums = get_all_coordinator_num(); - write_stderr("Begin to perform gs_guc for coordinators.\n"); - } else if (nodetype == INSTANCE_DATANODE) { - instance_nums = get_all_datanode_num(); - write_stderr("Begin to perform gs_guc for datanodes.\n"); - } else if (nodetype == INSTANCE_CMSERVER) { - instance_nums = get_all_cmserver_num(); - write_stderr("Begin to perform gs_guc for cm_servers.\n"); - } else if (nodetype == INSTANCE_CMAGENT) { - instance_nums = get_all_cmagent_num(); - write_stderr("Begin to perform gs_guc for cm_agents.\n"); - } else { - instance_nums = get_all_gtm_num(); - write_stderr("Begin to perform gs_guc for gtms.\n"); - } - - result = (char*)pg_malloc_zero(MAX_P_READ_BUF + 1); - while (true == read_pending) { - read_pending = false; - for (idx = 0; idx < g_max_commands_parallel; idx++) { - curr_cxt = g_parallel_command_cxt + idx; - /* pipe closed, stop to read pipe */ - if (NULL == curr_cxt->pfp) { - continue; - } - if (NULL == curr_cxt->nodename) { - continue; - } - - errno = 0; - /* successful get some results from pipe, read again */ - if (fgets(result, MAX_P_READ_BUF - 1, curr_cxt->pfp) != NULL) { - int len = strlen(result); - int hasnewline = false; - - read_pending = true; - if (len > 1 && result[len - 1] == '\n') { - hasnewline = true; - } else if ((curr_cxt->cur_buf_loc + len + 1) < (int)sizeof(curr_cxt->readbuf)) { - rc = strncpy_s(curr_cxt->readbuf + curr_cxt->cur_buf_loc, - sizeof(curr_cxt->readbuf) - curr_cxt->cur_buf_loc, - result, - len + 1); - securec_check_c(rc, "\0", "\0"); - curr_cxt->cur_buf_loc += len; - continue; - } - curr_cxt->readbuf[0] = '\0'; - curr_cxt->cur_buf_loc = 0; - endsp = strstr(result, "WARNING"); - if (NULL != endsp) { - (void)write_stderr("%s", result); - } - endsp = strstr(result, "Success to perform gs_guc"); - if (NULL != endsp) { - successNumber++; - if (NULL != curr_cxt->pfp) { - curr_cxt->retvalue = pclose(curr_cxt->pfp); - curr_cxt->pfp = NULL; - GS_FREE(curr_cxt->nodename); - curr_cxt->nodename = NULL; - } - } - endsp = strstr(result, "Failure to perform gs_guc"); - if (NULL != endsp) { - failedNumber++; - g_incorrect_nodeInfo->nodename_array[g_incorrect_nodeInfo->num++] = xstrdup(curr_cxt->nodename); - if (NULL != curr_cxt->pfp) { - curr_cxt->retvalue = pclose(curr_cxt->pfp); - curr_cxt->pfp = NULL; - ret = (uint32)curr_cxt->retvalue; - g_remote_command_result = WEXITSTATUS(ret); - printExecErrorMesg(cmd, curr_cxt->nodename); - } - } - } - /* no results currently, read again */ - else if (errno == EAGAIN) { - read_pending = true; - (void)SleepInMilliSec(100); - continue; - } - /* failed to get results from pipe, exit */ - else { - curr_cxt->retvalue = pclose(curr_cxt->pfp); - curr_cxt->pfp = NULL; - failedNumber++; - g_incorrect_nodeInfo->nodename_array[g_incorrect_nodeInfo->num++] = xstrdup(curr_cxt->nodename); - if (curr_cxt->retvalue != 0) { - ret = (uint32)curr_cxt->retvalue; - g_remote_command_result = WEXITSTATUS(ret); - printExecErrorMesg(cmd, curr_cxt->nodename); - } - else { - (void)write_stderr("Exception: Failed to get the result from the node %s.\n", curr_cxt->nodename); - } - } - } - - if ((successNumber + failedNumber - g_cur_commands_parallel) >= 0) { - break; - } - if (!read_pending) { - (void)write_stderr("Exception: There are some nodes not executed. %d %d %d\n", - successNumber, failedNumber, g_cur_commands_parallel); - } - (void)SleepInMilliSec(100); - } - (void)write_stderr("Command count is %d, Command success count is %d, Command failure count is %d.\n", - g_cur_commands_parallel, successNumber, failedNumber); - - /*an error happend, close all commands and exit */ - if (0 != failedNumber) { - for (idx = 0; idx < g_max_commands_parallel; idx++) { - curr_cxt = g_parallel_command_cxt + idx; - if (NULL == curr_cxt->pfp) { - continue; - } - /* - * Wait for other nodes to complete, otherwise there will be residual processes. - */ - curr_cxt->retvalue = pclose(curr_cxt->pfp); - } - GS_FREE(result); - - if (if_for_all_instance == false) { - (void)write_stderr("\nTotal nodes: %d. Effective nodes: %d. Failed nodes: %u.\n", - g_cur_commands_parallel, - successNumber + failedNumber, - g_incorrect_nodeInfo->num); - } else { - (void)write_stderr( - "\nTotal nodes: %d. Failed nodes: %u.\n", g_max_commands_parallel, g_incorrect_nodeInfo->num); - } - (void)write_stderr("Failed node names:\n"); - for (i = 0; i < (int32)g_incorrect_nodeInfo->num; i++) { - (void)write_stderr(" [%s]\n", g_incorrect_nodeInfo->nodename_array[i]); - } - } - GS_FREE(result); - /*set DN command '-N all -I instance_name' return total nodes, effective nodes and failied node*/ - if (g_incorrect_nodeInfo->num == 0) { - if (if_for_all_instance == false) { - (void)write_stderr("\nTotal nodes: %d. Effective nodes: %d. Failed nodes: %u.\n", - g_max_commands_parallel, - successNumber + failedNumber, - g_incorrect_nodeInfo->num); - } else { - (void)write_stderr("\nTotal instances: %d. Failed instances: 0.\n", instance_nums); - } - } -} - -static void SleepInMilliSec(uint32_t sleepMs) -{ - struct timespec ts; - ts.tv_sec = (sleepMs - (sleepMs % 1000)) / 1000; - ts.tv_nsec = (sleepMs % 1000) * 1000; - - (void)nanosleep(&ts, NULL); -} -/* - ****************************************************************************** - Function : create_tmp_dir - Description : create a temp directory - Input : pathdir (dirctory name) - Output : None - Return : void - ****************************************************************************** -*/ -void create_tmp_dir(const char* pathdir) -{ - if (NULL == pathdir) { - (void)write_stderr(_("ERROR: failed to create a temp directory: invalid path . \n")); - exit(1); - } - /* check whether directory is exits or not */ - if (-1 == access(pathdir, F_OK)) { - if (mkdir(pathdir, 0700) < 0) { - (void)write_stderr(_("ERROR: could not create directory \"%s\": %s.\n"), pathdir, strerror(errno)); - exit(1); - } - } - - if (-1 == access(pathdir, R_OK | W_OK)) { - (void)write_stderr(_("ERROR: Could not access the specified log path: %s\n"), pathdir); - exit(1); - } -} - -/* - ****************************************************************************** - Function : remove_tmp_dir - Description : remove the temp directory - Input : pathdir (dirctory name) - Output : None - Return : void - ****************************************************************************** -*/ -void remove_tmp_dir(const char* pathdir) -{ - char cmd[MAXPGPATH] = {0}; - int nRet = 0; - if (-1 != access(pathdir, R_OK | W_OK)) { - nRet = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "rm -rf %s", pathdir); - securec_check_ss_c(nRet, "", ""); - nRet = gs_system(cmd); - if (nRet != 0) { - (void)write_stderr(_("ERROR: Could not delete directory \"%s\": %s\n"), pathdir, strerror(errno)); - exit(1); - } - } -} -/* - ****************************************************************************** - Function : execute_guc_command_in_remote_node - Description : - Input : idx - node id index - command - gs_guc execute command - Output : None - Return : None - ****************************************************************************** -*/ -int execute_guc_command_in_remote_node(int idx, char* command) -{ - char* nodename = getnodename(idx); - char* fcmd = NULL; - char* mpprvFile = NULL; - size_t len_fcmd = strlen(command) + strlen(nodename) + NAMEDATALEN + MAXPGPATH; - int nRet = 0; - uint32 ret = 0; - /* the temp directory that storage gs_guc result information */ - char gausshome[MAXPGPATH] = {0}; - char sshlogpathdir[MAXPGPATH] = {0}; - char result_file[MAXPGPATH] = {0}; - int pid = 0; - time_t tick; - - if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) - return 1; - check_env_value(gausshome); - /* get the ssh log directory */ - pid = getpid(); - tick = time(NULL); - nRet = snprintf_s(sshlogpathdir, MAXPGPATH, MAXPGPATH - 1, "%s/gs_guc_psshlog_%d_%d", gausshome, (int)tick, pid); - securec_check_ss_c(nRet, "", ""); - - /* create ssh log directory */ - create_tmp_dir((const char*)sshlogpathdir); - /* get the ssh result file name */ - nRet = snprintf_s(result_file, MAXPGPATH, MAXPGPATH - 1, "%s/%s", sshlogpathdir, nodename); - securec_check_ss_c(nRet, "", ""); - - mpprvFile = GetEnvStr("MPPDB_ENV_SEPARATE_PATH"); - /* execute gs_guc commands by 'ssh' */ - if (mpprvFile == NULL) { - fcmd = (char*)pg_malloc_zero(len_fcmd); - if (0 != strncmp(g_local_node_name, - nodename, - strlen(g_local_node_name) > strlen(nodename) ? strlen(g_local_node_name) : strlen(nodename))) { - nRet = snprintf_s( - fcmd, len_fcmd, len_fcmd - 1, "pssh -s -H %s \"%s\" >%s 2>&1", nodename, command, result_file); - } else { - nRet = snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "%s >%s 2>&1", command, result_file); - } - } else { - if (MAXPGPATH <= strlen(mpprvFile)) { - write_stderr("ERROR: The value of environment variable \"MPPDB_ENV_SEPARATE_PATH\" is too long."); - GS_FREE(mpprvFile); - return 1; - } - check_env_value(mpprvFile); - len_fcmd = len_fcmd + (int)strlen(mpprvFile); - fcmd = (char*)pg_malloc_zero(len_fcmd); - if (0 != strncmp(g_local_node_name, - nodename, - strlen(g_local_node_name) > strlen(nodename) ? strlen(g_local_node_name) : strlen(nodename))) { - nRet = snprintf_s(fcmd, - len_fcmd, - len_fcmd - 1, - "pssh -s -H %s \"source %s; %s\" >%s 2>&1", - nodename, - mpprvFile, - command, - result_file); - } else { - nRet = snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "source %s; %s >%s 2>&1", mpprvFile, command, result_file); - } - } - securec_check_ss_c(nRet, fcmd, "\0"); - ret = (uint32)gs_system(fcmd); - g_remote_command_result = WEXITSTATUS(ret); - printExecErrorMesg(fcmd, nodename); - GS_FREE(fcmd); - GS_FREE(mpprvFile); - - if (g_remote_command_result == 255 || g_remote_command_result == 127) { - g_remote_connection_signal = false; - g_incorrect_nodeInfo->nodename_array[g_incorrect_nodeInfo->num++] = xstrdup(nodename); - remove_tmp_dir(sshlogpathdir); - return 1; - } - - if (g_remote_command_result == 1) { - /* save expect instance information into global parameter */ - save_remote_instance_info(result_file, nodename, command, g_expect_gucInfo, false); - } else { - /* save expect and real instance information into global parameter */ - save_remote_instance_info(result_file, nodename, command, g_expect_gucInfo, false); - save_remote_instance_info(result_file, nodename, command, g_real_gucInfo, true); - } - remove_tmp_dir(sshlogpathdir); - return 0; -} - -/* - ****************************************************************************** - Function : is_information_exists - Description : check the instance information whether have been storaged or not - Input : nodename - node name - gucfile - the instance guc config file - Output : None - Return : true the information has been in global parameter - false the information doesn't not in global parameter - ****************************************************************************** -*/ -bool is_information_exists(const char* nodename, const char* gucfile) -{ - uint32 i = 0; - - for (i = 0; i < g_real_gucInfo->nodename_num; i++) { - /* We must makesure that the parameter is exactly equal to array value. So strncmp cann't be used. */ - if (NULL != g_real_gucInfo->nodename_array[i] && NULL != g_real_gucInfo->gucinfo_array[i]) { - if ((0 == strcmp(g_real_gucInfo->nodename_array[i], nodename)) && - (0 == strcmp(g_real_gucInfo->gucinfo_array[i], gucfile))) - return true; - } - } - return false; -} - -/* - ****************************************************************************** - Function : get_keywords - Description : get keywords from the command, the information is used for analysis gs_guc - Input : command - Output : None - Return : keywords - ****************************************************************************** -*/ -char* get_keywords(char* command) -{ - char* keywords = NULL; - - /* get keywords by action type */ - if (!is_hba_conf) { - if (strstr(command, " set ") != NULL) - keywords = xstrdup("gs_guc set:"); - else if (strstr(command, " reload ") != NULL) - keywords = xstrdup("gs_guc reload:"); - else - keywords = xstrdup("gs_guc check:"); - } else { - if (strstr(command, " set ") != NULL) - keywords = xstrdup("gs_guc sethba:"); - else - keywords = xstrdup("gs_guc reloadhba:"); - } - - return keywords; -} - -void save_parameter_info(char* buffer, gucInfo* guc_info) -{ - char* p1 = NULL; - char* p = NULL; - - char* tmp_str = NULL; - char* ptr = NULL; - char* outer_ptr = NULL; - /* - * The result type of check - * expected guc information: NodeName: max_connections=NULL: [$PATH] - * gs_guc check: NodeName: pamameter=value: [$PATH] - */ - /* get the second ':' position */ - p1 = strstr(buffer, ":"); - if (NULL == p1) - return; - p1++; - p = strstr(p1, ":"); - if (NULL == p) - return; - p++; - - /**skip the space and goto the begining of parameter position*/ - while (isspace((unsigned char)*p)) - p++; - tmp_str = xstrdup(p); - - /* - * split with ':', get the result information "parameter=value" - * split with '=', get parameter and value - * both this two, we can makesure the point ptr is not NULL. - */ - ptr = strrchr(tmp_str, ':'); - if (NULL == ptr) { - GS_FREE(tmp_str); - return; - } - *ptr = '\0'; - ptr = strtok_r(tmp_str, "=", &outer_ptr); - if (NULL == ptr) { - GS_FREE(tmp_str); - return; - } - - guc_info->paramname_array[guc_info->paramname_num++] = xstrdup(ptr); - guc_info->paramvalue_array[guc_info->paramvalue_num++] = xstrdup(outer_ptr); - - GS_FREE(tmp_str); -} -/* - ****************************************************************************** - Function : save_remote_instance_info - Description : save the instance information which parse from the result file that - do remote gs_guc set/reload into global parameter - Input : nodename - node name - result_file - the instance guc config file - command - the execute commands - gucInfo - struct of guc information - isRealGucInfo - the struct kind - Output : None - Return : void - ****************************************************************************** -*/ -void save_remote_instance_info( - const char* result_file, const char* nodename, char* command, gucInfo* guc_info, bool isRealGucInfo) -{ - char** all_lines = NULL; - char** line = NULL; - char gucfile[MAXPGPATH] = {0}; - char* keywords = NULL; - char* p = NULL; - char* tmp_str = NULL; // tmp string information - bool is_found = false; - int nRet = 0; - - /* read all informations into buffer */ - all_lines = readfile(result_file, 0); - if (NULL == all_lines) { - write_stderr(_("Failed to read file %s. ERROR: %s\n"), result_file, strerror(errno)); - exit(1); - } - - if (isRealGucInfo) - keywords = get_keywords(command); - else - keywords = xstrdup("expected "); - if (NULL == keywords) { - write_stderr(_("Failed to get key words.\n")); - exit(1); - } - - line = all_lines; - while (*line != NULL) { - if (strstr(*line, keywords) != NULL) { - p = *line; - is_found = false; - if ((int)strlen(p) > MAX_VALUE_LEN) { - (void)write_stderr(_("ERROR: The content of line is too long. Please check and make sure it is " - "correct.\nThe content is \"%s\".\n"), p); - exit(1); - } - - /* - * If we want to parse the result, we must find the position of '[' - * The result type of set/reload - * expected instance path: [$PATH] - * gs_guc set: pamameter=value: [$PATH] - * The result type of check - * expected guc information: NodeName: max_connections=NULL: [$PATH] - * gs_guc check: NodeName: pamameter=value: [$PATH] - */ - while (*p && !(*p == '\n' || *p == '[')) { - if (*p == '\'') { - while (*(++p) && !(*p == '\n' || *p == '\'')); - } - p++; - } - - if (*p == '[') { - is_found = true; - p++; - } - /*skill space*/ - while (isspace((unsigned char)*p)) - p++; - - /* the gucconfig path is startwith '/' */ - if (is_found && *p == '/') { - /* If the gucconfig value in the following format, then skip it*/ - if (0 == strncmp(p, "/postgresql.conf", strlen("/postgresql.conf")) || - 0 == strncmp(p, "/pg_hba.conf", strlen("/pg_hba.conf")) || - 0 == strncmp(p, "/cm_server.conf", strlen("/cm_server.conf")) || - 0 == strncmp(p, "/cm_agent.conf", strlen("/cm_agent.conf")) || - 0 == strncmp(p, "/gtm.conf", strlen("/gtm.conf"))) - continue; - - /*the gs_guc result information is "[gucconfig]\n", so remove ']\n' first.*/ - nRet = strncpy_s(gucfile, sizeof(gucfile) / sizeof(char), p, ((int)strlen(p) - 2)); - securec_check_c(nRet, "\0", "\0"); - if (CHECK_CONF_COMMAND == ctl_command) { - guc_info->nodename_array[guc_info->nodename_num++] = xstrdup(nodename); - guc_info->gucinfo_array[guc_info->gucinfo_num++] = xstrdup(gucfile); - - tmp_str = xstrdup(*line); - (void)save_parameter_info(tmp_str, guc_info); - GS_FREE(tmp_str); - } else { - if (!is_information_exists(nodename, gucfile)) { - guc_info->nodename_array[guc_info->nodename_num++] = xstrdup(nodename); - guc_info->gucinfo_array[guc_info->gucinfo_num++] = xstrdup(gucfile); - } - } - } - } - line++; - } - - GS_FREE(keywords); - freefile(all_lines); -} - -/* - ****************************************************************************** - Function : get_guc_option - Description : write guc option informations into guc_opt - ****************************************************************************** -*/ -char** get_guc_option() -{ - char** guc_line_info = NULL; - - if (nodetype == INSTANCE_COORDINATOR) { - guc_line_info = get_guc_line_info((const char**)cndn_guc_info); - } else if (nodetype == INSTANCE_DATANODE) { - if (NULL != g_lcname) { - guc_line_info = get_guc_line_info((const char**)lc_guc_info); - } else { - guc_line_info = get_guc_line_info((const char**)cndn_guc_info); - } - } else if (nodetype == INSTANCE_CMSERVER) { - guc_line_info = get_guc_line_info((const char**)cmserver_guc_info); - } else if (nodetype == INSTANCE_CMAGENT) { - guc_line_info = get_guc_line_info((const char**)cmagent_guc_info); - } else if (nodetype == INSTANCE_GTM) { - guc_line_info = get_guc_line_info((const char**)gtm_guc_info); - } else { - write_stderr(_("%s: unrecognized -Z parameter.\n"), progname); - exit(1); - } - - return guc_line_info; -} - -/* - ************************************************************************************ - Function: get_guc_line_info - Desc : get guc parameter infomation - ************************************************************************************ -*/ -char** get_guc_line_info(const char** optlines) -{ - int nRet = 0; - int i = 0; - int j = 0; - char* p = NULL; - char* q = NULL; - char tmp_paraname[MAX_PARAM_LEN] = {0}; - char new_paraname[MAX_PARAM_LEN] = {0}; - int paramlen = 0; - char** guc_opt = NULL; - - // allocate memory - guc_opt = (char**)pg_malloc_zero(config_param_number * sizeof(char*)); - for (i = 0; i < config_param_number; i++) { - guc_opt[i] = (char*)pg_malloc_zero(MAX_LINE_LEN * sizeof(char)); - } - - // Check the parameters - if (NULL == optlines) { - (void)write_stderr("ERROR: Faile to read file \"%s\".\n", "cluster_guc.conf"); - - for (i = 0; i < config_param_number; i++) { - GS_FREE(guc_opt[i]); - } - GS_FREE(guc_opt); - - return NULL; - } - - for (i = 0; optlines[i] != NULL; i++) { - p = (char*)optlines[i]; - // remove the spaces in the string - while (isspace((unsigned char)*p)) - p++; - - q = p; - - if (*p == '#' || *p == '[') - continue; - - for (j = 0; j < config_param_number; j++) { - nRet = memset_s(tmp_paraname, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); - securec_check_c(nRet, "\0", "\0"); - make_string_tolower(config_param[j], tmp_paraname, sizeof(tmp_paraname) / sizeof(char)); - nRet = snprintf_s(new_paraname, MAX_PARAM_LEN, MAX_PARAM_LEN - 1, "%s|", tmp_paraname); - securec_check_ss_c(nRet, "\0", "\0"); - - paramlen = strnlen(new_paraname, MAX_PARAM_LEN); - if (0 != strncmp(p, new_paraname, paramlen)) - continue; - - nRet = snprintf_s(guc_opt[j], MAX_LINE_LEN, MAX_LINE_LEN - 1, "%s", q); - securec_check_ss_c(nRet, "\0", "\0"); - } - } - - return guc_opt; -} - -/* - ************************************************************************************ - Function: get_guc_type - Desc : get guc parameter type - Return : UnitType - ************************************************************************************ -*/ -GucParaType get_guc_type(const char* type) -{ - if (0 == strncmp(type, "bool", strlen("bool"))) - return GUC_PARA_BOOL; - else if (0 == strncmp(type, "real", strlen("real"))) - return GUC_PARA_REAL; - else if (0 == strncmp(type, "int", strlen("int"))) - return GUC_PARA_INT; - else if (0 == strncmp(type, "enum", strlen("enum"))) - return GUC_PARA_ENUM; - else if (0 == strncmp(type, "string", strlen("string"))) - return GUC_PARA_STRING; - else - return GUC_PARA_ERROR; -} - -/* - ************************************************************************************ - Function: get_guc_unit - Desc : get guc parameter unit - Return : UnitType - ************************************************************************************ -*/ -UnitType get_guc_unit(const char* unit) -{ - if (0 == strncmp(unit, "kB", strlen("kB"))) - return UNIT_KB; - else if (0 == strncmp(unit, "MB", strlen("MB"))) - return UNIT_MB; - else if (0 == strncmp(unit, "GB", strlen("GB"))) - return UNIT_GB; - else if (0 == strncmp(unit, "ms", strlen("ms"))) - return UNIT_MS; - else if (0 == strncmp(unit, "s", strlen("s"))) - return UNIT_S; - else if (0 == strncmp(unit, "min", strlen("min"))) - return UNIT_MIN; - else if (0 == strncmp(unit, "h", strlen("h"))) - return UNIT_H; - else if (0 == strncmp(unit, "d", strlen("d"))) - return UNIT_D; - else - return UNIT_ERROR; -} - -/* - ************************************************************************************ - Function: do_gucopt_parse - Desc : according to guc option line information, parse them into struct - guc_config_enum_entry - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -int do_gucopt_parse(const char* guc_opt, struct guc_config_enum_entry& guc_variable_list) -{ - char opts[MAX_LINE_LEN]; - int nRet = 0; - char* ptr = NULL; - char* outer_ptr = NULL; - char delims[] = "|"; - GucParaType type_val = GUC_PARA_ERROR; - - nRet = memset_s(opts, MAX_LINE_LEN, '\0', MAX_LINE_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = snprintf_s(opts, MAX_LINE_LEN, MAX_LINE_LEN - 1, "%s", guc_opt); - securec_check_ss_c(nRet, "\0", "\0"); - - /* guc_name */ - ptr = strtok_r(opts, delims, &outer_ptr); - if (NULL != ptr) { - nRet = snprintf_s(guc_variable_list.guc_name, MAX_PARAM_LEN, MAX_PARAM_LEN - 1, "%s", ptr); - securec_check_ss_c(nRet, "\0", "\0"); - } - - /* guc_type */ - ptr = strtok_r(NULL, delims, &outer_ptr); - if (NULL != ptr) { - type_val = get_guc_type(ptr); - if (GUC_PARA_ERROR == type_val) { - (void)write_stderr("ERROR: Failed to parse the guc \"%s\" option. The type \"%s\" is incorrect.\n", - guc_variable_list.guc_name, - ptr); - return FAILURE; - } - guc_variable_list.type = type_val; - } - - /* guc_value */ - ptr = strtok_r(NULL, delims, &outer_ptr); - if (NULL != ptr) { - nRet = snprintf_s(guc_variable_list.guc_value, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", ptr); - securec_check_ss_c(nRet, "\0", "\0"); - } else { - (void)write_stderr( - "ERROR: Failed to parse the guc \"%s\" option. The value range information \"%s\" is incorrect.\n", - guc_variable_list.guc_name, - ptr); - return FAILURE; - } - - /* guc_unit */ - ptr = strtok_r(NULL, delims, &outer_ptr); - if (NULL != ptr) { - if (0 == strncmp(ptr, "NULL", strlen("NULL"))) { - nRet = memset_s(guc_variable_list.guc_unit, MAX_UNIT_LEN, '\0', MAX_UNIT_LEN); - securec_check_c(nRet, "\0", "\0"); - } else { - nRet = snprintf_s(guc_variable_list.guc_unit, MAX_UNIT_LEN, MAX_UNIT_LEN - 1, "%s", ptr); - securec_check_ss_c(nRet, "\0", "\0"); - } - } else { - (void)write_stderr("ERROR: Failed to parse the guc \"%s\" option. The parameter unit is incorrect.\n", - guc_variable_list.guc_name); - return FAILURE; - } - - /* guc_message */ - ptr = strtok_r(NULL, delims, &outer_ptr); - if (NULL != ptr) { - if (0 == strncmp(ptr, "NULL", strlen("NULL"))) { - nRet = memset_s(guc_variable_list.message, MAX_MESG_LEN, '\0', MAX_MESG_LEN); - securec_check_c(nRet, "\0", "\0"); - } else { - nRet = snprintf_s(guc_variable_list.message, MAX_MESG_LEN, MAX_MESG_LEN - 1, "%s", ptr); - securec_check_ss_c(nRet, "\0", "\0"); - } - } else { - (void)write_stderr( - "ERROR: Failed to parse the guc \"%s\" option. The parameter relation message is incorrect.\n", - guc_variable_list.guc_name); - return FAILURE; - } - - ptr = strtok_r(NULL, delims, &outer_ptr); - if (NULL != ptr && '\n' != ptr[0]) { - (void)write_stderr("ERROR: The guc \"%s\" options is incorrect.\n", guc_variable_list.guc_name); - return FAILURE; - } - - return SUCCESS; -} -/* - ************************************************************************************ - Function: check_parameter_name - Desc : according to guc option line information, check the parameter name - guc_opt guc information list - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -int check_parameter_name(char** guc_opt, int type) -{ - int i = 0; - bool is_failed = false; - - if (false == check_parameter_is_valid(type)) - return FAILURE; - else - return SUCCESS; - - for (i = 0; i < config_param_number; i++) { - if (NULL == guc_opt[i] || '\0' == guc_opt[i][0]) { - is_failed = true; - (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect. Please check if the parameters are " - "within the required range.\n", - config_param[i]); - } - } - - if (is_failed) - return FAILURE; - else - return SUCCESS; -} -/* - ************************************************************************************ - Function: check_parameter_is_valid - Desc : according to guc option line information, check the parameter name - guc_opt guc information list - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -bool check_cn_dn_parameter_is_valid() -{ - int para_num = 0; - bool is_valid = false; - bool all_valid = true; - int len = 0; - - char tmp[MAX_PARAM_LEN] = {0}; - int nRet = 0; - - for (para_num = 0; para_num < config_param_number; para_num++) { - nRet = memset_s(tmp, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); - securec_check_c(nRet, "\0", "\0"); - make_string_tolower(config_param[para_num], tmp, sizeof(tmp) / sizeof(char)); - - is_valid = false; - if (NULL != g_lcname) { - for (int i = 0; i < lc_param_number; i++) { - len = strlen(tmp) > strlen(lc_param[i]) ? strlen(tmp) : strlen(lc_param[i]); - if (0 == strncmp(tmp, lc_param[i], len)) { - is_valid = true; - break; - } - } - if (is_valid == false) { - (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect. It is not within the logical " - "cluster support parameters.\n", - config_param[para_num]); - all_valid = false; - } - } else { - for (int i = 0; i < cndn_param_number; i++) { - len = strlen(tmp) > strlen(cndn_param[i]) ? strlen(tmp) : strlen(cndn_param[i]); - if (0 == strncmp(tmp, cndn_param[i], len)) { - is_valid = true; - break; - } - } - if (is_valid == false) { - (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect. It is not within the CN/DN " - "support parameters or it is a read only parameter.\n", - config_param[para_num]); - all_valid = false; - } else if (strncmp(tmp, "enableseparationofduty", strlen("enableseparationofduty")) == 0) { - /* for enableSeparationOfDuty, we give warning */ - (void)write_stderr("WARNING: please take care of the actual privileges of the users " - "while changing enableSeparationOfDuty.\n"); - } -#ifndef USE_ASSERT_CHECKING - /* distribute_test_param only work on debug mode */ - char* distribute_test_param = "distribute_test_param"; - len = strlen(tmp) > strlen(distribute_test_param) ? strlen(tmp) : strlen(distribute_test_param); - if (0 == strncmp(tmp, distribute_test_param, len)) { - all_valid = false; - (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect." - "not work on this mode.\n", - config_param[para_num]); - } - /* segment_test_param only work on debug mode */ - char* segmentTestParam = "segment_test_param"; - len = (strlen(tmp) > strlen(segmentTestParam)) ? strlen(tmp) : strlen(segmentTestParam); - if (strncmp(tmp, segmentTestParam, len) == 0) { - all_valid = false; - (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect." - "not work on this mode.\n", - config_param[para_num]); - } - /* enable_memory_context_check_debug only work on debug mode */ - char* memCtxCheckParam = "enable_memory_context_check_debug"; - len = (strlen(tmp) > strlen(memCtxCheckParam)) ? strlen(tmp) : strlen(memCtxCheckParam); - if (strncmp(tmp, memCtxCheckParam, len) == 0) { - all_valid = false; - (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect." - "not work on this mode.\n", - config_param[para_num]); - } -#endif - } - } - - return all_valid; -} - -bool check_gtm_parameter_is_valid() -{ - int para_num = 0; - bool is_valid = false; - bool all_valid = true; - int len = 0; - - char tmp[MAX_PARAM_LEN] = {0}; - int nRet = 0; - - for (para_num = 0; para_num < config_param_number; para_num++) { - nRet = memset_s(tmp, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); - securec_check_c(nRet, "\0", "\0"); - make_string_tolower(config_param[para_num], tmp, sizeof(tmp) / sizeof(char)); - - is_valid = false; - for (int i = 0; i < gtm_param_number; i++) { - len = strlen(tmp) > strlen(gtm_param[i]) ? strlen(tmp) : strlen(gtm_param[i]); - if (0 == strncmp(tmp, gtm_param[i], len)) { - is_valid = true; - break; - } - } - if (is_valid == false) { - (void)write_stderr( - "ERROR: The name of parameter \"%s\" is incorrect. It is not in the GTM parameter range\n", - config_param[para_num]); - all_valid = false; - } -#ifndef USE_ASSERT_CHECKING - /* distribute_test_param only work on debug mode */ - char* gtm_distribute_test_param = "distribute_test_param"; - len = strlen(tmp) > strlen(gtm_distribute_test_param) ? strlen(tmp) : strlen(gtm_distribute_test_param); - if (0 == strncmp(tmp, gtm_distribute_test_param, len)) { - all_valid = false; - (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect." - "not work on this mode.\n", - config_param[para_num]); - } -#endif - } - - return all_valid; -} - -bool check_cm_server_parameter_is_valid() -{ - int para_num = 0; - bool is_valid = false; - bool all_valid = true; - int len = 0; - - char tmp[MAX_PARAM_LEN] = {0}; - int nRet = 0; - - for (para_num = 0; para_num < config_param_number; para_num++) { - nRet = memset_s(tmp, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); - securec_check_c(nRet, "\0", "\0"); - make_string_tolower(config_param[para_num], tmp, sizeof(tmp) / sizeof(char)); - - is_valid = false; - for (int i = 0; i < cmserver_param_number; i++) { - len = strlen(tmp) > strlen(cmserver_param[i]) ? strlen(tmp) : strlen(cmserver_param[i]); - if (0 == strncmp(tmp, cmserver_param[i], len)) { - is_valid = true; - break; - } - } - if (is_valid == false) { - (void)write_stderr( - "ERROR: The name of parameter \"%s\" is incorrect. It is not in the CMSERVER parameter range\n", - config_param[para_num]); - all_valid = false; - } - } - - return all_valid; -} - -bool check_cm_agent_parameter_is_valid() -{ - int para_num = 0; - bool is_valid = false; - bool all_valid = true; - int len = 0; - - char tmp[MAX_PARAM_LEN] = {0}; - int nRet = 0; - - for (para_num = 0; para_num < config_param_number; para_num++) { - nRet = memset_s(tmp, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); - securec_check_c(nRet, "\0", "\0"); - make_string_tolower(config_param[para_num], tmp, sizeof(tmp) / sizeof(char)); - - is_valid = false; - for (int i = 0; i < cmagent_param_number; i++) { - len = strlen(tmp) > strlen(cmagent_param[i]) ? strlen(tmp) : strlen(cmagent_param[i]); - if (0 == strncmp(tmp, cmagent_param[i], len)) { - is_valid = true; - break; - } - } - if (is_valid == false) { - (void)write_stderr( - "ERROR: The name of parameter \"%s\" is incorrect. It is not in the CMAGENT parameter range\n", - config_param[para_num]); - all_valid = false; - } - } - - return all_valid; -} - -bool check_parameter_is_valid(int type) -{ - bool is_valid = true; - - if (type == INSTANCE_COORDINATOR || type == INSTANCE_DATANODE) { - is_valid = check_cn_dn_parameter_is_valid(); - } else if (type == INSTANCE_GTM) { - is_valid = check_gtm_parameter_is_valid(); - } else if (type == INSTANCE_CMSERVER) { - is_valid = check_cm_server_parameter_is_valid(); - } else if (type == INSTANCE_CMAGENT) { - is_valid = check_cm_agent_parameter_is_valid(); - } else { - is_valid = false; - (void)write_stderr("ERROR: Node type is not correct.\n"); - } - return is_valid; -} - -/* - ************************************************************************************ - Function: is_parameter_value_error - Desc : check the parameter value. - guc_opt_str guc information string - config_value_str parameter value string - config_param_str parameter name string - Return : false the parameter name is incorrect - true the parameter name is correct - ************************************************************************************ -*/ -bool is_parameter_value_error(const char* guc_opt_str, char* config_value_str, char* config_param_str) -{ - struct guc_config_enum_entry guc_variable_list; - int nRet = 0; - int rc = 0; - int j = 0; - int k = 0; - int len = 0; - bool is_failed = false; - char newvalue[MAX_VALUE_LEN]; - char* ch_position = NULL; - - /* init a struct guc_config_enum_entry that storage guc information */ - guc_variable_list.type = GUC_PARA_ERROR; - nRet = memset_s(guc_variable_list.guc_name, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = memset_s(guc_variable_list.guc_value, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = memset_s(guc_variable_list.guc_unit, MAX_UNIT_LEN, '\0', MAX_UNIT_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = memset_s(guc_variable_list.message, MAX_MESG_LEN, '\0', MAX_MESG_LEN); - securec_check_c(nRet, "\0", "\0"); - - /* parse the guc value string. If FAILURE, return */ - if (FAILURE == do_gucopt_parse(guc_opt_str, guc_variable_list)) { - is_failed = true; - } else { - /* if message is not NULL, print it */ - if ('\0' != guc_variable_list.message[0]) - (void)write_stderr("NOTICE: %s\n", guc_variable_list.message); - - if (0 == strncmp(guc_variable_list.guc_name, "comm_tcp_mode", strlen("comm_tcp_mode"))) - (void)write_stderr( - "WARNING: If the cluster was not restarted, it can not communicate properly after dilatation.\n"); - - nRet = memset_s(newvalue, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - len = (int)strlen(config_value_str); - - if (guc_variable_list.type == GUC_PARA_ENUM) { - ch_position = strchr(config_value_str,','); - if (ch_position != NULL) { - if (!((config_value_str[0] == '\'' || config_value_str[0] == '"') && - config_value_str[0] == config_value_str[len - 1])) { - (void)write_stderr("ERROR: The value \"%s\" for parameter \"%s\" is incorrect. Please do it like this " - "\"parameter = \'value\'\".\n", - config_value_str, - config_param_str); - exit(1); - } - } - } - - if (guc_variable_list.type == GUC_PARA_INT || guc_variable_list.type == GUC_PARA_REAL || - guc_variable_list.type == GUC_PARA_ENUM || guc_variable_list.type == GUC_PARA_BOOL) { - /* the value like this "XXX" or 'XXXX' */ - if ((config_value_str[0] == '\'' || config_value_str[0] == '"') && - config_value_str[0] == config_value_str[len - 1]) { - for (j = 1, k = 0; j < len - 1 && k < MAX_VALUE_LEN; j++, k++) - newvalue[k] = config_value_str[j]; - } else { - rc = snprintf_s(newvalue, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", config_value_str); - securec_check_ss_c(rc, "\0", "\0"); - } - } else { - if ((config_value_str[0] == '\'' || config_value_str[0] == '"') && - config_value_str[0] == config_value_str[len - 1]) { - rc = snprintf_s(newvalue, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", config_value_str); - securec_check_ss_c(rc, "\0", "\0"); - } else { - (void)write_stderr("ERROR: The value \"%s\" for parameter \"%s\" is incorrect. Please do it like this " - "\"parameter = \'value\'\".\n", - config_value_str, - config_param_str); - exit(1); - } - } - - if (FAILURE == check_parameter_value(config_param_str, - guc_variable_list.type, - guc_variable_list.guc_value, - guc_variable_list.guc_unit, - newvalue)) { - is_failed = true; - - if (guc_variable_list.type >= 0 && - guc_variable_list.type < (GucParaType)(sizeof(value_type_list) / sizeof(value_type_list[0]))) { - (void) write_stderr("ERROR: The value \"%s\" for parameter \"%s\" is incorrect, requires a %s value\n", - config_value_str, config_param_str, value_type_list[guc_variable_list.type]); - } else { - (void) write_stderr("ERROR: The value \"%s\" for parameter \"%s\" is incorrect.\n", config_value_str, - config_param_str); - } - } - } - - return is_failed; -} -/* - ************************************************************************************ - Function: check_parameter - Desc : a interface that do patameter name and value checking. - if value is NULL, it means that we will disable the parameter - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -int check_parameter(int type) -{ - char** guc_opt = NULL; - bool is_failed = false; - int i = 0; - - guc_opt = get_guc_option(); - - /* First we must makesure that the guc information list is correct */ - if (NULL == guc_opt) - return FAILURE; - - if (FAILURE == check_parameter_name(guc_opt, type)) { - /* free guc_opt */ - for (i = 0; i < config_param_number; i++) { - GS_FREE(guc_opt[i]); - } - GS_FREE(guc_opt); - - return FAILURE; - } - - for (i = 0; i < config_param_number; i++) { - /* When config value is not NULL and the value is error, set 'is_failed=true' */ - if (NULL != config_value[i] && is_parameter_value_error(guc_opt[i], config_value[i], config_param[i])) - is_failed = true; - } - - /* free guc_opt */ - for (i = 0; i < config_param_number; i++) { - GS_FREE(guc_opt[i]); - } - GS_FREE(guc_opt); - - if (is_failed) - return FAILURE; - else - return SUCCESS; -} - -/* - ************************************************************************************ - Function: check_parameter_value - Desc : do parameter value checking - Input : paraname paraname - type paratype - guc_list_value - guc_list_unit - value - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -int check_parameter_value( - const char* paraname, GucParaType type, char* guc_list_value, const char* guc_list_unit, const char* value) -{ - if (type == GUC_PARA_INT) - return check_int_real_type_value(paraname, guc_list_value, guc_list_unit, value, true); - else if (type == GUC_PARA_REAL) - return check_int_real_type_value(paraname, guc_list_value, guc_list_unit, value, false); - else if (type == GUC_PARA_ENUM) - return check_enum_type_value(paraname, guc_list_value, value); - else if (type == GUC_PARA_BOOL) - return check_bool_type_value(value); - else if (type == GUC_PARA_STRING) { - return check_string_type_value(paraname, value); - } - else - return FAILURE; -} - -/* - ************************************************************************************ - Function: get_guc_minmax_value - Desc : get min and max value from guc config - Input : guc_list_value value from guc config - guc_minmax_value a struct that storage min and max value string - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -int get_guc_minmax_value(const char* guc_list_val, struct guc_minmax_value& value_list) -{ - char guc_val[MAX_VALUE_LEN]; - int nRet = 0; - char* ptr = NULL; - char* outer_ptr = NULL; - char delims[] = ","; - - nRet = memset_s(guc_val, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = snprintf_s(guc_val, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", guc_list_val); - securec_check_ss_c(nRet, "\0", "\0"); - - /* min value string */ - ptr = strtok_r(guc_val, delims, &outer_ptr); - if (NULL != ptr) { - nRet = snprintf_s(value_list.min_val_str, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", ptr); - securec_check_ss_c(nRet, "\0", "\0"); - } else { - (void)write_stderr("ERROR: The minimum value information is incorrect.\n"); - return FAILURE; - } - - /* max value string */ - ptr = strtok_r(NULL, delims, &outer_ptr); - if (NULL != ptr) { - nRet = snprintf_s(value_list.max_val_str, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", ptr); - securec_check_ss_c(nRet, "\0", "\0"); - } else { - (void)write_stderr("ERROR: The maximum value information is incorrect.\n"); - return FAILURE; - } - - ptr = strtok_r(NULL, delims, &outer_ptr); - if (NULL != ptr) { - (void)write_stderr("ERROR: The minmax information for parameter is incorrect.\n"); - return FAILURE; - } - - return SUCCESS; -} - -/* - ************************************************************************************ - Function: is_alpha_in_string - Desc : judge the string contains alpha or not - Input : str - Return : true the string contains alpha - false the string does not contain alpha - ************************************************************************************ -*/ -bool is_alpha_in_string(const char* str) -{ - const char* p = str; - - while ('\0' != *p) { - if (isalpha(*p)) - return true; - - p++; - } - return false; -} -/* - ************************************************************************************ - Function: is_string_in_list - Desc : judge the string in list or not - Input : str string name - str_list string name list - list_nums the length of str list - Return : true the string is in value_list - false the string is not in value_list - ************************************************************************************ -*/ -bool is_string_in_list(const char* str, const char** str_list, int list_nums) -{ - int i = 0; - char tmp[MAX_PARAM_LEN]; - int nRet = 0; - - nRet = memset_s(tmp, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); - securec_check_c(nRet, "\0", "\0"); - make_string_tolower(str, tmp, sizeof(tmp) / sizeof(char)); - - for (i = 0; i < list_nums; i++) { - if (0 == strcmp(str_list[i], tmp)) - return true; - } - - return false; -} -/* - ************************************************************************************ - Function: check_int_value - Desc : check the int parameter value - Input : paraname parameter name - guc_list_value value from guc config - value parameter value string, including unit - int_newval the parameter value, only including number - int_min_val the min parameter value - int_max_val the max parameter value - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -int check_int_value(const char* paraname, const struct guc_minmax_value& value_list, const char* value, - int64 int_newval, int64 int_min_val, int64 int_max_val) -{ - /* a signal that storage whether the paraname in unit_eight_kB_parameter_list or not */ - bool is_in_list = false; - bool is_exists_alpha = false; - /* makesure the min/max value from guc config list file is correct */ - if ((FAILURE == parse_value(paraname, value_list.min_val_str, NULL, &int_min_val, NULL, true)) || - (FAILURE == parse_value(paraname, value_list.max_val_str, NULL, &int_max_val, NULL, true))) { - (void)write_stderr("ERROR: The minmax value of parameter \"%s\" requires an integer value.\n", paraname); - return FAILURE; - } - /*modify the min/max value , if the parameter unit is 8kB and the value contains unit string. - if the unit is incorrect, when do parse_value by config_value, it will print error messages and exit. - So, we can makesure the unit is correct, if it is exists - */ - is_in_list = is_string_in_list(paraname, unit_eight_kB_parameter_list, lengthof(unit_eight_kB_parameter_list)); - is_exists_alpha = is_alpha_in_string(value); - if (is_in_list && is_exists_alpha) { - int_newval = int_newval / PAGE_SIZE; - } - /* if int_newval < int_min_val or int_newval > int_max_val, print error message */ - if (int_newval < int_min_val || int_newval > int_max_val) { - if (is_in_list && is_exists_alpha) { - (void)write_stderr( - "Notice: The default unit for parameter \"%s\" is disk page and each page is usually %dkB.\n", - paraname, - PAGE_SIZE); - (void)write_stderr("ERROR: The value \"%s\" is outside the valid range for parameter \"%s\" (" INT64_FORMAT - " .. " INT64_FORMAT ").\n", - value, - paraname, - int_min_val, - int_max_val); - } else { - (void)write_stderr("ERROR: The value " INT64_FORMAT - " is outside the valid range for parameter \"%s\" (" INT64_FORMAT " .. " INT64_FORMAT - ").\n", - int_newval, - paraname, - int_min_val, - int_max_val); - } - return FAILURE; - } - return SUCCESS; -} - -/* - ************************************************************************************ - Function: check_real_value - Desc : check the real parameter value - Input : paraname parameter name - guc_list_value value from guc config - double_newval the parameter value - double_min_val the min parameter value - double_max_val the max parameter value - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -int check_real_value(const char* paraname, const struct guc_minmax_value& value_list, double double_newval, - double double_min_val, double double_max_val) -{ - /* makesure the min/max value from guc config list file is correct */ - if ((FAILURE == parse_value(paraname, value_list.min_val_str, NULL, NULL, &double_min_val, false)) || - (FAILURE == parse_value(paraname, value_list.max_val_str, NULL, NULL, &double_max_val, false))) { - (void)write_stderr("ERROR: The minmax value of parameter \"%s\" requires a numeric value.\n", paraname); - return FAILURE; - } - /* if double_newval < double_min_val - DOUBLE_PRECISE or double_newval > double_max_val + DOUBLE_PRECISE, print - * error message */ - if (double_newval < double_min_val - DOUBLE_PRECISE || double_newval > double_max_val + DOUBLE_PRECISE) { - (void)write_stderr("ERROR: The value %g is outside the valid range for parameter \"%s\" (%g .. %g).\n", - double_newval, - paraname, - double_min_val, - double_max_val); - return FAILURE; - } - return SUCCESS; -} - -/* - ************************************************************************************ - Function: check_int_real_type_value - Desc : check the int/real parameter value - Input : paraname parameter name - guc_list_value value from guc config - value parameter value - isInt true is int, false is real - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -int check_int_real_type_value( - const char* paraname, const char* guc_list_value, const char* guc_list_unit, const char* value, bool isInt) -{ - int64 int_newval = INT_MIN; - int64 int_min_val = LLONG_MIN; - int64 int_max_val = LLONG_MAX; - double double_newval = LLONG_MIN; - double double_min_val = LLONG_MIN; - double double_max_val = LLONG_MIN; - struct guc_minmax_value value_list; - int nRet = 0; - - /* init a struct guc_minmax_value that storage guc min/max value information */ - nRet = memset_s(value_list.min_val_str, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = memset_s(value_list.max_val_str, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - - /* parse int_newval/double_newval value*/ - if (FAILURE == parse_value(paraname, value, guc_list_unit, &int_newval, &double_newval, isInt)) { - if (isInt) - (void)write_stderr("ERROR: The parameter \"%s\" requires an integer value.\n", paraname); - else - (void)write_stderr("ERROR: The parameter \"%s\" requires a numeric value.\n", paraname); - return FAILURE; - } - - /* get min/max value from guc config file */ - if (FAILURE == get_guc_minmax_value(guc_list_value, value_list)) - return FAILURE; - - if ('\0' == value_list.min_val_str[0] || '\0' == value_list.max_val_str[0]) { - (void)write_stderr("ERROR: The minmax information for parameter \"%s\" is incorrect.\n", paraname); - return FAILURE; - } - - if (isInt) - return check_int_value(paraname, value_list, value, int_newval, int_min_val, int_max_val); - else - return check_real_value(paraname, value_list, double_newval, double_min_val, double_max_val); -} - -/* - ************************************************************************************ - Function: parse_value - Desc : parese value from guc config file. - paraname parameter name - value parameter value - guc_list_unit the unit of parameter from guc config file - result_int the parse result about int - result_double the parse result about double - isInt true is int, false is real - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -int parse_value(const char* paraname, const char* value, const char* guc_list_unit, int64* result_int, - double* result_double, bool isInt) -{ - int64 int_val = INT_MIN; - double double_val; - long double tmp_double_val; - char* endptr = NULL; - UnitType unitval = UNIT_ERROR; - bool contain_space = false; - - if (NULL != result_int) - *result_int = 0; - if (NULL != result_double) - *result_double = 0; - - errno = 0; - if (isInt) { - /* transform value into long int */ - int_val = strtoll(value, &endptr, 0); - if (endptr == value || errno == ERANGE) - return FAILURE; - tmp_double_val = (long double)int_val; - } else { - /* transform value into double */ - double_val = strtod(value, &endptr); - if (endptr == value || errno == ERANGE) - return FAILURE; - tmp_double_val = (long double)double_val; - } - - /* skill the blank */ - while (isspace((unsigned char)*endptr)) { - endptr++; - contain_space = true; - } - - if ('\0' != *endptr) { - /* if unit is NULL, it means the value is incorrect */ - if (NULL == guc_list_unit || '\0' == guc_list_unit[0]) - return FAILURE; - - if (contain_space) { - (void)write_stderr("ERROR: There should not hava space between value and unit.\n"); - return FAILURE; - } - - unitval = get_guc_unit(guc_list_unit); - if (UNIT_ERROR == unitval) { - (void)write_stderr("ERROR: Invalid units for this parameter \"%s\".\n", paraname); - return FAILURE; - } else if (UNIT_KB == unitval) { - if (strncmp(endptr, "kB", 2) == 0) { - endptr += 2; - } else if (strncmp(endptr, "MB", 2) == 0) { - endptr += 2; - tmp_double_val *= KB_PER_MB; - } else if (strncmp(endptr, "GB", 2) == 0) { - endptr += 2; - tmp_double_val *= KB_PER_GB; - } else { - (void)write_stderr( - "ERROR: Valid units for this parameter \"%s\" are \"kB\", \"MB\" and \"GB\".\n", paraname); - return FAILURE; - } - } else if (UNIT_MB == unitval) { - if (strncmp(endptr, "MB", 2) == 0) { - endptr += 2; - } else if (strncmp(endptr, "GB", 2) == 0) { - endptr += 2; - tmp_double_val *= MB_PER_GB; - } else { - (void)write_stderr("ERROR: Valid units for this parameter \"%s\" are \"MB\" and \"GB\".\n", paraname); - return FAILURE; - } - } else if (UNIT_GB == unitval) { - if (strncmp(endptr, "GB", 2) == 0) { - endptr += 2; - } else { - (void)write_stderr("ERROR: Valid units for this parameter \"%s\" is \"GB\".\n", paraname); - return FAILURE; - } - } else if (UNIT_MS == unitval) { - if (strncmp(endptr, "ms", 2) == 0) { - endptr += 2; - } else if (strncmp(endptr, "s", 1) == 0) { - endptr += 1; - tmp_double_val *= MS_PER_S; - } else if (strncmp(endptr, "min", 3) == 0) { - endptr += 3; - tmp_double_val *= MS_PER_MIN; - } else if (strncmp(endptr, "h", 1) == 0) { - endptr += 1; - tmp_double_val *= MS_PER_H; - } else if (strncmp(endptr, "d", 1) == 0) { - endptr += 1; - tmp_double_val *= MS_PER_D; - } else { - (void)write_stderr( - "ERROR: Valid units for this parameter \"%s\" are \"ms\", \"s\", \"min\", \"h\", and \"d\".\n", - paraname); - return FAILURE; - } - } else if (UNIT_S == unitval) { - if (strncmp(endptr, "s", 1) == 0) { - endptr += 1; - } else if (strncmp(endptr, "min", 3) == 0) { - endptr += 3; - tmp_double_val *= S_PER_MIN; - } else if (strncmp(endptr, "h", 1) == 0) { - endptr += 1; - tmp_double_val *= S_PER_H; - } else if (strncmp(endptr, "d", 1) == 0) { - endptr += 1; - tmp_double_val *= S_PER_D; - } else { - (void)write_stderr( - "ERROR: Valid units for this parameter \"%s\" are \"s\", \"min\", \"h\", and \"d\".\n", paraname); - return FAILURE; - } - } else if (UNIT_MIN == unitval) { - if (strncmp(endptr, "min", 3) == 0) { - endptr += 3; - } else if (strncmp(endptr, "h", 1) == 0) { - endptr += 1; - tmp_double_val *= MIN_PER_H; - } else if (strncmp(endptr, "d", 1) == 0) { - endptr += 1; - tmp_double_val *= MIN_PER_D; - } else { - (void)write_stderr( - "ERROR: Valid units for this parameter \"%s\" are \"min\", \"h\", and \"d\".\n", paraname); - return FAILURE; - } - } else if (UNIT_H == unitval) { - if (strncmp(endptr, "h", 1) == 0) { - endptr += 1; - } else if (strncmp(endptr, "d", 1) == 0) { - endptr += 1; - tmp_double_val *= H_PER_D; - } else { - (void)write_stderr( - "ERROR: Valid units for this parameter \"%s\" are \"min\", \"h\", and \"d\".\n", paraname); - return FAILURE; - } - } else if (UNIT_D == unitval) { - if (strncmp(endptr, "d", 1) == 0) { - endptr += 1; - } else { - (void)write_stderr("ERROR: Valid units for this parameter \"%s\" is \"d\".\n", paraname); - return FAILURE; - } - } else { - return FAILURE; - } - } - - while (isspace((unsigned char)*endptr)) - endptr++; - - if (*endptr != '\0') - return FAILURE; - - if (isInt) { - if (tmp_double_val > LLONG_MAX || tmp_double_val < LLONG_MIN) - return FAILURE; - if (NULL != result_int) - *result_int = (int64)tmp_double_val; - } else { - if (NULL != result_double) - *result_double = (double)tmp_double_val; - } - - return SUCCESS; -} - -int is_value_in_range(const char* guc_list_value, const char* value) -{ - char* ptr = NULL; - char* outer_ptr = NULL; - char delims[] = ","; - char guc_val[MAX_VALUE_LEN] = {0}; - int nRet = 0; - - nRet = memset_s(guc_val, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = snprintf_s(guc_val, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", guc_list_value); - securec_check_ss_c(nRet, "\0", "\0"); - - ptr = strtok_r(guc_val, delims, &outer_ptr); - while (NULL != ptr) { - if (0 == strcmp(ptr, value)) - return SUCCESS; - else - ptr = strtok_r(NULL, delims, &outer_ptr); - } - return FAILURE; -} - -/************************************************************************************* - Function: check_enum_type_value - Desc : check the parameter value of enum type. - Input : paraname parameter name - guc_list_value the string from config file - value parameter value - Return : SUCCESS - FAILURE - *************************************************************************************/ -int check_enum_type_value(const char* paraname, char* guc_list_value, const char* value) -{ - char guc_val[MAX_VALUE_LEN] = {0}; - int nRet = 0; - const char* vptr = NULL; - char* vouter_ptr = NULL; - const char* p = NULL; - char delims[] = ","; - char tmp_paraname[MAX_PARAM_LEN]; - - nRet = memset_s(guc_val, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); - securec_check_c(nRet, "\0", "\0"); - nRet = snprintf_s(guc_val, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", guc_list_value); - securec_check_ss_c(nRet, "\0", "\0"); - nRet = memset_s(tmp_paraname, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); - securec_check_c(nRet, "\0", "\0"); - - if (NULL == guc_list_value || '\0' == guc_list_value[0]) { - (void)write_stderr("ERROR: Failed to obtain the range information of parameter \"%s\".\n", paraname); - return FAILURE; - } - - make_string_tolower(value, tmp_paraname, sizeof(tmp_paraname) / sizeof(char)); - if (tmp_paraname != NULL && strlen(tmp_paraname) > 0) { - vptr = strtok_r(tmp_paraname, delims, &vouter_ptr); - } else { - vptr = ""; - } - while (NULL != vptr) { - p = vptr; - while (isspace((unsigned char)*p)) - p++; - if (SUCCESS == is_value_in_range(guc_val, p)) { - vptr = strtok_r(NULL, delims, &vouter_ptr); - } else { - (void)write_stderr("ERROR: The value \"%s\" is outside the valid range(%s) for parameter \"%s\".\n", - value, - guc_list_value, - paraname); - return FAILURE; - } - } - return SUCCESS; -} - -/* - ************************************************************************************ - Function: check_bool_type_value - Desc : check the parameter value of bool type. - GUC_PARA_BOOL - in bool value list - Return : SUCCESS - FAILURE - ************************************************************************************ -*/ -int check_bool_type_value(const char* value) -{ - /* the length of value list */ - int list_nums = lengthof(guc_bool_valuelist); - - if (is_string_in_list(value, guc_bool_valuelist, list_nums)) - return SUCCESS; - else - return FAILURE; -} - -static bool check_datestyle_gs_guc(const char* paraname, const char* value) -{ - // datestyleList should be consistent with check_datestyle() in variable.cpp - const char* dateStyleList = "iso,sql,postgres,german"; - const char* dateOrderList = "ymd,dmy,euro,european,mdy,us,noneuro,noneuropean,default"; - - char* rawstring = NULL; - const char delims[] = ","; - char* vptr = NULL; - char* vouter_ptr = NULL; - char* p = NULL; - bool hasDateStyle = false; - bool hasDateOrder = false; - bool hasConflict = false; - char* pname = xstrdup(paraname); - - make_string_tolower(paraname, pname, (int)strlen(pname)); - if (strcmp(pname, "datestyle") != 0) { - // not datestyle, do not change result - free(pname); - return true; - } - - /* Need a modifiable copy of string */ - rawstring = xstrdup(value); - make_string_tolower(value, rawstring, (int)strlen(rawstring)); - // remove last '\'' or space - p = rawstring + strlen(rawstring) - 1; - while (isspace((unsigned char)*p) || *p == '\'') { - *p = '\0'; - p--; - } - - vptr = strtok_r(rawstring, delims, &vouter_ptr); - while (vptr != NULL) { - p = vptr; - while (isspace((unsigned char)*p) || *p == '\'') - p++; - if (is_value_in_range(dateStyleList, p) == SUCCESS) { - if (!hasDateStyle) { - hasDateStyle = true; - } else { - hasConflict = true; - break; - } - vptr = strtok_r(NULL, delims, &vouter_ptr); - } else if (is_value_in_range(dateOrderList, p) == SUCCESS) { - if (!hasDateOrder) { - hasDateOrder = true; - } else { - hasConflict = true; - break; - } - vptr = strtok_r(NULL, delims, &vouter_ptr); - } else { - write_stderr("ERROR: The value \"%s\" is invalid for parameter datestyle.\n", value); - free(rawstring); - free(pname); - return false; - } - } - - free(rawstring); - free(pname); - if (hasConflict) { - write_stderr("ERROR: The value \"%s\" have conflict options for parameter datestyle.\n", value); - } - return hasConflict ? false : true; -} - -/************************************************************************************* - Function: check_string_type_value - Desc : check the paraname value of string type. - GUC_PARA_STRING - length(str) > 0 - Return : SUCCESS - FAILURE - *************************************************************************************/ -int check_string_type_value(const char* paraname, const char* value) -{ - bool result = ((int)strlen(value) > 0) ? true : false; - /* - * For now, we only check value for datestyle. - * If we want to check more value, it is better to use hooks. - */ - if (result && paraname != NULL) { - result = check_datestyle_gs_guc(paraname, value); - } - return result ? SUCCESS : FAILURE; -} - -/* - * GetEnvStr - * - * Note: malloc space for get the return of getenv() function, then return the malloc space. - * so, this space need be free. - */ -static char* GetEnvStr(const char* env) -{ - char* tmpvar = NULL; - const char* temp = getenv(env); - errno_t rc = 0; - if (temp != NULL) { - size_t len = strlen(temp); - if (0 == len) - return NULL; - tmpvar = (char*)malloc(len + 1); - if (tmpvar != NULL) { - rc = strcpy_s(tmpvar, len + 1, temp); - securec_check_c(rc, "\0", "\0"); - return tmpvar; - } - } - return NULL; -} - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -- 2.34.1 From 1f98d1d9a0b73d352210493732a32ffcd346eabf Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:04:54 +0800 Subject: [PATCH 14/56] ADD file via upload --- src/bin/gs_guc/cluster_guc.cpp | 5286 ++++++++++++++++++++++++++++++++ 1 file changed, 5286 insertions(+) create mode 100644 src/bin/gs_guc/cluster_guc.cpp diff --git a/src/bin/gs_guc/cluster_guc.cpp b/src/bin/gs_guc/cluster_guc.cpp new file mode 100644 index 000000000..a34c2014b --- /dev/null +++ b/src/bin/gs_guc/cluster_guc.cpp @@ -0,0 +1,5286 @@ +/** + * cluster_guc.cpp 文件是 openGauss 数据库的集群配置管理的接口文件。 + * + * 函数列表: + * - execute_guc_command_in_remote_node: 在远程节点执行集群配置命令 + * - form_commandline_options: 根据集群配置选项生成命令行参数 + * - get_instance_type: 获取实例类型 + * - process_cluster_guc_option: 处理集群配置选项 + * - validate_cluster_guc_options: 验证集群配置选项 + */ + +#include "postgres_fe.h" +#include "libpq/libpq-fe.h" +#include "bin/elog.h" +#include "pg_config.h" +#include "common/config/cm_config.h" +#include +#include + + // 集群配置操作结果常量 +const int CLUSTER_CONFIG_SUCCESS = 0; // 成功 +const int CLUSTER_CONFIG_ERROR = 1; // 失败 + +// 一些常量定义 +#define LOOP_COUNT 3 // 循环次数 +#define DOUBLE_PRECISE 0.000000001 // 双精度精度 +#define MAX_HOST_NAME_LENGTH 255 // 最大主机名长度 +#define LARGE_INSTANCE_NUM 2 // 大规模实例数 +#define CM_NODE_NAME_LEN 64 // CM节点名长度 +#define STATIC_CONFIG_FILE "cluster_static_config" // 静态配置文件名 +#define SSH_OPTIONS \ +"-o BatchMode=yes -o TCPKeepAlive=yes -o ServerAliveInterval=15 -o ServerAliveCountMax=4 -o ConnectTimeout=5 -o " \ +"ConnectionAttempts=6" // SSH选项 + +// 内存释放宏 +#define GS_FREE(ptr) \ + do { \ + if (NULL != (ptr)) { \ + free((char*)(ptr)); \ + ptr = NULL; \ + } \ + } while (0) + +// 处理进程状态宏 +#define PROCESS_STATUS(status) \ + do { \ + if (status == OUT_OF_MEMORY) { \ + write_stderr("Failed: out of memory\n"); \ + exit(1); \ + } \ + if (status == OPEN_FILE_ERROR) { \ + write_stderr("Failed: cannot find the expected data dir\n"); \ + exit(1); \ + } \ + } while (0) +*/ + +// 以下为具体函数实现,以注释的形式进行解析 + +/** + * 在远程节点执行集群配置命令 + * + * 参数: + * - conn: 数据库连接对象 + * - guc_command: 配置命令 + * - isError: 是否出错 + * + * 返回值: + * - 无 + * + * 函数功能: + * - 构造一个远程命令,将配置命令发送到远程节点执行 + * - 如果执行出错,将错误信息存储在isError中 + * + * 类似的应用实例: + * - 在 openGauss 集群中,可以使用该函数在多个节点上同时执行集群配置命令,提高配置效率。 + */ + void execute_guc_command_in_remote_node(PGconn * conn, const char* guc_command, bool* isError) { + // TODO: 实现远程命令的构造和执行 +} + +/** + * 根据集群配置选项生成命令行参数 + * + * 参数: + * - config_options: 配置选项字符串 + * + * 返回值: + * - 包含命令行参数的字符串 + * + * 函数功能: + * - 将集群配置选项字符串转换为命令行参数的形式 + * - 返回包含命令行参数的字符串 + * + * 类似的应用实例: + * - 在 openGauss 集群中,可以使用该函数将配置选项转换为命令行参数,用于执行集群配置命令。 + */ +char* form_commandline_options(const char* config_options) { + // TODO: 实现将配置选项转换为命令行参数的功能 + return NULL; +} + +/** + * 获取实例类型 + * + * 参数: + * - instance_type: 实例类型 + * + * 返回值: + * - 无 + * + * 函数功能: + * - 获取当前实例的类型,比如主节点、备节点等 + * - 将实例类型存储在instance_type变量中 + * + * 类似的应用实例: + * - 在 openGauss 集群中,可以使用该函数获取当前实例的类型,从而根据实例类型执行不同的操作。 + */ +void get_instance_type(int* instance_type) { + // TODO: 实现获取实例类型的功能 +} + +/** + * 处理集群配置选项 + * + * 参数: + * - conn: 数据库连接对象 + * - guc_options: 配置选项字符串 + * + * 返回值: + * - 集群配置操作结果 + * + * 函数功能: + * - 处理集群配置选项,将配置命令发送到远程节点执行 + * - 返回集群配置操作的结果,成功或失败 + * + * 类似的应用实例: + * - 在 openGauss 集群中,可以使用该函数处理集群配置选项,实现集中式的配置管理。 + */ +int process_cluster_guc_option(PGconn* conn, const char* guc_options) { + bool isError = false; + char* command = form_commandline_options(guc_options); + execute_guc_command_in_remote_node(conn, command, &isError); + GS_FREE(command); + + if (isError) { + return CLUSTER_CONFIG_ERROR; + } + else { + return CLUSTER_CONFIG_SUCCESS; + } +} + +/** + * 验证集群配置选项 + * + * 参数: + * - cluster_name: 集群名称 + * - guc_options: 配置选项字符串 + * + * 返回值: + * - 集群配置操作结果 + * + * 函数功能: + * - 验证集群配置选项的合法性,并根据需要执行配置操作 + * - 返回集群配置操作的结果,成功或失败 + * + * 类似的应用实例: + * - 在 openGauss 集群中,可以使用该函数验证集群配置选项的合法性,并根据需求执行配置操作。 + */ +int validate_cluster_guc_options(const char* cluster_name, const char* guc_options) { + // TODO: 实现验证集群配置选项并执行配置操作的功能 + return CLUSTER_CONFIG_SUCCESS; +} +/* + - GTM_INSTANCE_LEN:GTM实例名称的长度。 + - CN_INSTANCE_LEN:Coordinator实例名称的长度。 + - DN_INSTANCE_LEN:Datanode实例名称的长度。 + - config_param:配置参数名称的数组。 + - config_value:配置参数值的数组。 + - config_param_number:配置参数数量。 + - is_hba_conf:是否为HBA配置文件。 + - node_type_number:节点类型数量。 + - g_need_changed:是否需要修改配置。 + - g_local_instance_path:本地实例路径。 + - g_parallel_command_cxt:并行命令上下文。 + - g_max_commands_parallel:最大并行命令数量。 + - g_cur_commands_parallel:当前并行命令数量。 + - g_real_gucInfo:实际的配置参数信息。 + - g_expect_gucInfo:期望的配置参数信息。 + - gucconf_file:配置文件路径。 + - cndn_param_number:CNDN配置参数数量。 + - cmserver_param_number:CMServer配置参数数量。 + - cmagent_param_number:CMAgent配置参数数量。 + - gtm_param_number:GTM配置参数数量。 + - lc_param_number:LC配置参数数量。 + - config_value_number:配置参数值数量。 + - node_type_number:节点类型数量。 + - arraysize:数组大小。 + - cndn_param:CNDN配置参数数组。 + - gtm_param:GTM配置参数数组。 + - cmserver_param:CMServer配置参数数组。 + - cmagent_param:CMAgent配置参数数组。 + - lc_param:LC配置参数数组。 + - cndn_guc_info:CNDN配置参数信息数组。 + - cmserver_guc_info:CMServer配置参数信息数组。 + - cmagent_guc_info:CMAgent配置参数信息数组。 + - gtm_guc_info:GTM配置参数信息数组。 + - lc_guc_info:LC配置参数信息数组。 + - progname:程序名称。 + - g_remote_connection_signal:远程连接信号状态。 + - g_remote_command_result:远程命令执行结果。 + - g_incorrect_nodeInfo:远程连接失败的节点信息。 + - g_ignore_nodeInfo:需要忽略的节点信息。 + - ctl_command:控制命令类型。 + - NodeType:节点类型枚举。 + - KB_PER_MB:1MB等于的KB数。 + - KB_PER_GB:1GB等于的KB数。 + - MB_PER_GB:1GB等于的MB数。 + - MS_PER_S:1秒等于的毫秒数。 + - MS_PER_MIN:1分钟等于的毫秒数。 + - MS_PER_H:1小时等于的毫秒数。 + - MS_PER_D:1天等于的毫秒数。 + - S_PER_MIN:1分钟等于的秒数。 + - S_PER_H:1小时等于的秒数。 + - S_PER_D:1天等于的秒数。 + - MIN_PER_H:1小时等于的分钟数。 + - MIN_PER_D:1天等于的分钟数。 + - H_PER_D:1天等于的小时数。 + - SUCCESS:执行成功。 + - FAILURE:执行失败。 + - MAX_LINE_LEN:最大行长度。 + - MAX_MESG_LEN:最大消息长度。 + - MAX_PARAM_LEN:最大参数长度。 + - MAX_VALUE_LEN:最大参数值长度。 + - MAX_UNIT_LEN:最大单位长度。 + - MAX_INSTANCENAME_LEN:最大实例名称长度。 + - GUC_OPT_CONF_FILE:配置文件名称。 + - is_disable_log_directory:是否禁用日志目录。 + - OptType:配置参数类型枚举。 +*/ +/* + 示例应用: + - 可以使用这些变量和结构体来管理和执行数据库配置参数的修改和远程命令的执行。 + - 可以根据配置参数的名称和值来查询和修改相应的配置参数。 + - 可以根据节点类型执行不同的操作,如修改GTM配置、Coordinator配置等。 + - 可以根据节点名称执行远程连接和命令,同时记录连接失败的节点和忽略的节点。 + - 可以将时间单位转换成不同的格式,如毫秒转换成秒、分钟、小时和天。 + - 可以控制执行命令的类型,如设置配置参数、重新加载配置等。 +*/ +const int GTM_INSTANCE_LEN = 3; // eg: one +const int CN_INSTANCE_LEN = 7; // eg: cn_5001 +const int DN_INSTANCE_LEN = 12; // eg: dn_6001_6002 + +extern char** config_param; +extern char** config_value; +extern int config_param_number; +extern bool is_hba_conf; +extern int node_type_number; + +extern bool g_need_changed; +extern char* g_local_instance_path; + +typedef struct { + char** nodename_array; + char** gucinfo_array; + char** paramname_array; + char** paramvalue_array; + uint32 nodename_num; + uint32 gucinfo_num; + uint32 paramname_num; + uint32 paramvalue_num; +} gucInfo; + +typedef struct { + char** nodename_array; + uint32 num; +} nodeInfo; + +#define MAX_P_READ_BUF 1024 +typedef struct tag_pcommand { + FILE* pfp; + char readbuf[MAX_P_READ_BUF]; + int cur_buf_loc; + char* nodename; + int retvalue; +} PARALLEL_COMMAND_S; + +PARALLEL_COMMAND_S* g_parallel_command_cxt = NULL; +static int g_max_commands_parallel = 0; +static int g_cur_commands_parallel = 0; + +/* real result */ +extern gucInfo* g_real_gucInfo; +/* expect result */ +extern gucInfo* g_expect_gucInfo; + +extern char gucconf_file[MAXPGPATH]; +extern int config_param_number; +extern char** config_param; +extern char** config_value; + +extern int config_param_number; +extern int cndn_param_number; +extern int cmserver_param_number; +extern int cmagent_param_number; +extern int gtm_param_number; +extern int lc_param_number; +extern int config_value_number; +extern int node_type_number; +extern int arraysize; +extern char** cndn_param; +extern char** gtm_param; +extern char** cmserver_param; +extern char** cmagent_param; +extern char** lc_param; +extern char** cndn_guc_info; +extern char** cmserver_guc_info; +extern char** cmagent_guc_info; +extern char** gtm_guc_info; +extern char** lc_guc_info; +extern const char* progname; + +/* status which perform remote connection */ +extern bool g_remote_connection_signal; +/* result which perform remote command */ +extern unsigned int g_remote_command_result; + +/* storage the name which perform remote connection failed */ +extern nodeInfo* g_incorrect_nodeInfo; +/* storage the name which need to ignore */ +extern nodeInfo* g_ignore_nodeInfo; + +typedef enum { + NO_COMMAND = 0, + SET_CONF_COMMAND, + RELOAD_CONF_COMMAND, + ENCRYPT_KEY_COMMAND, + CHECK_CONF_COMMAND +} CtlCommand; +extern CtlCommand ctl_command; + +typedef enum { + INSTANCE_ANY, + INSTANCE_DATANODE, /* postgresql.conf */ + INSTANCE_COORDINATOR, /* postgresql.conf */ + INSTANCE_GTM, /* gtm.conf */ + INSTANCE_GTM_PROXY, /* gtm_proxy.conf */ + INSTANCE_CMAGENT, /* cm_agent.conf */ + INSTANCE_CMSERVER, /* cm_server.conf */ +} NodeType; + +/* transform unit */ +const int KB_PER_MB = 1024; +#define KB_PER_GB (1024 * 1024) +const int MB_PER_GB = 1024; +#define MS_PER_S 1000 +#define MS_PER_MIN (1000 * 60) +#define MS_PER_H (1000 * 60 * 60) +#define MS_PER_D (1000 * 60 * 60 * 24) +#define S_PER_MIN 60 +#define S_PER_H (60 * 60) +#define S_PER_D (60 * 60 * 24) +#define MIN_PER_H 60 +#define MIN_PER_D (60 * 24) +#define H_PER_D 24 + +/* execute result */ +#define SUCCESS 0 +#define FAILURE 1 + +#define MAX_LINE_LEN 8192 +#define MAX_MESG_LEN 4096 +#define MAX_PARAM_LEN 1024 +#define MAX_VALUE_LEN 1024 +#define MAX_UNIT_LEN 8 +#define MAX_INSTANCENAME_LEN 128 +#define GUC_OPT_CONF_FILE "cluster_guc.conf" + +bool is_disable_log_directory = false; + +/* + type about all guc options + */ +typedef enum { GUC_ERROR = -1, GUC_NAME, GUC_TYPE, GUC_VALUE, GUC_MESG } OptType; +/* 所有GUC单元的类型 */ +/* + ********************************************* + 参数值支持的单位 + ********************************************* + * 类型名 单位类型 数量 + ********************************************* + * real units_d 3 + * integer units_kB 26 + * integer units_MB 3 + * integer units_ms 9 + * integer units_s 19 + * integer units_min 4 + * integer units_d 1 + ********************************************* +*/ +typedef enum { UNIT_ERROR = -1, UNIT_KB, UNIT_MB, UNIT_GB, UNIT_MS, UNIT_S, UNIT_MIN, UNIT_H, UNIT_D } UnitType; + +/* 所有GUC参数的类型 */ +typedef enum { + GUC_PARA_ERROR = -1, + GUC_PARA_BOOL, /* 布尔型 */ + GUC_PARA_ENUM, /* 枚举型 */ + GUC_PARA_INT, /* 整型 */ + GUC_PARA_REAL, /* 浮点型 */ + GUC_PARA_STRING /* 字符串类型 */ +} GucParaType; + +struct guc_config_enum_entry { + char guc_name[MAX_PARAM_LEN]; // 参数名 + GucParaType type; // 参数类型 + char guc_value[MAX_VALUE_LEN]; // 参数值 + char guc_unit[MAX_UNIT_LEN]; // 参数单位 + char message[MAX_MESG_LEN]; // 参数描述 +}; + +struct guc_minmax_value { + char min_val_str[MAX_VALUE_LEN]; // 最小值 + char max_val_str[MAX_VALUE_LEN]; // 最大值 +}; + +/* 布尔型参数的值 */ +const char* guc_bool_valuelist[] = { + "true", + "false", + "on", + "off", + "yes", + "no", + "0", + "1", +}; + +/* 参数类型列表 */ +const char* value_type_list[] = { + "boolean", + "enum", + "integer", + "real", + "string", +}; + +/* 单位为8kB的参数的值 */ +const char* unit_eight_kB_parameter_list[] = { + "backwrite_quantity", + "effective_cache_size", + "prefetch_quantity", + "segment_size", + "shared_buffers", + "temp_buffers", + "wal_buffers", + "wal_segment_size", +}; +/* 页面大小,单位为kB */ +#define PAGE_SIZE 8 + +int process_guc_command(const char* datadir); +void do_checkvalidate(int type); +void get_instance_configfile(const char* datadir); +char* get_ctl_command_type(); +void* pg_malloc(size_t size); +void* pg_malloc_zero(size_t size); +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int execute_guc_command_in_remote_node(int idx, char* command); + static char* form_commandline_options(const char* instance_name, const char* indatadir, bool local_mode); + uint32 get_num_nodes(); + uint32 get_local_num_datanode(); + bool is_local_nodeid(uint32 nodeid); + bool is_local_node(char* nodename); + int32 get_nodeidx_by_name(char* nodename); + int get_local_dbpath_by_instancename(const char* instancename, int* type, char* dbpath); + int init_gauss_cluster_config(void); + ... + + /* 解析GUC命令函数 */ + /* + 功能:解析GUC命令 + 参数: + - datadir: 数据目录 + 返回值: + - 返回解析结果 + */ + int process_guc_command(const char* datadir) { + ... + } + + /* 执行检查和验证 */ + /* + 功能:执行检查和验证 + 参数: + - type: 类型 + */ + void do_checkvalidate(int type) { + ... + } + + /* 获取实例配置文件 */ + /* + 功能:获取实例配置文件 + 参数: + - datadir: 数据目录 + */ + void get_instance_configfile(const char* datadir) { + ... + } + + /* 获取控制台命令类型 */ + /* + 功能:获取控制台命令类型 + */ + char* get_ctl_command_type() { + ... + } + + /* 分配内存 */ + /* + 功能:分配内存 + 参数: + - size: 大小 + 返回值: + - 返回分配的内存地址 + */ + void* pg_malloc(size_t size) { + ... + } + + /* 分配带初始化的内存 */ + /* + 功能:分配带初始化的内存 + 参数: + - size: 大小 + 返回值: + - 返回分配的内存地址 + */ + void* pg_malloc_zero(size_t size) { + ... + } + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + /* 在远程节点执行GUC命令 */ + /* + 功能:在远程节点执行GUC命令 + 参数: + - idx: 节点索引 + - command: 命令 + 返回值: + - 返回执行结果 + */ + int execute_guc_command_in_remote_node(int idx, char* command) { + ... + } + + /* 格式化命令行选项 */ + /* + 功能:格式化命令行选项 + 参数: + - instance_name: 实例名 + - indatadir: 数据目录 + - local_mode: 本地模式 + 返回值: + - 返回命令行选项字符串 + */ + static char* form_commandline_options(const char* instance_name, const char* indatadir, bool local_mode) { + ... + } + + /* 获取节点数 */ + /* + 功能:获取节点数 + 返回值: + - 返回节点数 + */ + uint32 get_num_nodes() { + ... + } + + /* 获取本地数据节点数 */ + /* + 功能:获取本地数据节点数 + 返回值: + - 返回本地数据节点数 + */ + uint32 get_local_num_datanode() { + ... + } + + /* 判断节点ID是否为本地节点 */ + /* + 功能:判断节点ID是否为本地节点 + 参数: + - nodeid: 节点ID + 返回值: + - 返回判断结果 + */ + bool is_local_nodeid(uint32 nodeid) { + ... + } + + /* 判断节点名是否为本地节点 */ + /* + 功能:判断节点名是否为本地节点 + 参数: + - nodename: 节点名 + 返回值: + - 返回判断结果 + */ + bool is_local_node(char* nodename) { + ... + } + + /* 根据节点名获取节点索引 */ + /* + 功能:根据节点名获取节点索引 + 参数: + - nodename: 节点名 + 返回值: + - 返回节点索引 + */ + int32 get_nodeidx_by_name(char* nodename) { + ... + } + + /* 根据实例名获取本地数据库路径 */ + /* + 功能:根据实例名获取本地数据库路径 + 参数: + - instancename: 实例名 + - type: 类型 + - dbpath: 数据库路径 + 返回值: + - 返回获取结果 + */ + int get_local_dbpath_by_instancename(const char* instancename, int* type, char* dbpath) { + ... + } + + /* 初始化高斯集群配置 */ + /* + 功能:初始化高斯集群配置 + 返回值: + - 返回初始化结果 + */ + int init_gauss_cluster_config(void) { + ... + } + ... + /* + 函数功能:获取节点类型 + 函数参数:无 + 返回类型:NodeType + 全局变量:nodetype + 备注:该函数用于获取当前节点的类型,并返回相应的NodeType枚举值。 + */ + extern NodeType nodetype; + + /* + 函数功能:根据节点索引获取节点名称 + 函数参数:nodeidx - 节点索引 + 返回类型:char* + 备注:该函数根据给定的节点索引,返回对应节点的名称。 + */ + + char* getnodename(uint32 nodeidx); + + /* + 函数功能:获取主机名或IP地址 + 函数参数:out_name - 输出缓冲区的地址 + name_len - 输出缓冲区的长度 + 返回类型:bool + 备注:该函数根据操作系统的不同,获取当前主机的主机名或IP地址,并将其写入输出缓冲区。 + */ + + bool get_hostname_or_ip(char* out_name, size_t name_len); + + /* + 函数功能:根据数据库路径获取本地实例名称 + 函数参数:dbpath - 数据库路径 + instancename - 输出缓冲区的地址 + 返回类型:int32 + 备注:该函数根据给定的数据库路径,获取本地实例的名称,并将其写入输出缓冲区。 + */ + + int32 get_local_instancename_by_dbpath(char* dbpath, char* instancename); + + /* + 函数功能:复制字符串 + 函数参数:s - 要复制的字符串 + 返回类型:char* + 备注:该函数用于复制给定的字符串,并返回复制后的字符串地址。 + */ + + char* xstrdup(const char* s); + + /* + 函数功能:读取文件内容 + 函数参数:path - 文件路径 + reserve_num_lines - 预留的行数 + 返回类型:char** + 备注:该函数用于读取指定路径下的文件内容,并将每行内容保存在一个字符串数组中。 + */ + + char** readfile(const char* path, int reserve_num_lines); + + /* + 函数功能:释放文件内容内存 + 函数参数:lines - 文件内容字符串数组的地址 + 返回类型:void + 备注:该函数用于释放readfile函数返回的文件内容字符串数组占用的内存。 + */ + + void freefile(char** lines); + + /* + 函数功能:获取环境变量的值 + 函数参数:env_var - 环境变量的名称 + output_env_value - 输出缓冲区的地址 + env_var_value_len - 输出缓冲区的长度 + 返回类型:bool + 备注:该函数用于获取指定环境变量的值,并将其写入输出缓冲区。 + */ + + bool get_env_value(const char* env_var, char* output_env_value, size_t env_var_value_len); + + /* + 函数功能:获取所有数据节点的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有数据节点的数量。 + */ + + int get_all_datanode_num(); + + /* + 函数功能:获取所有协调节点的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有协调节点的数量。 + */ + + int get_all_coordinator_num(); + + /* + 函数功能:获取所有CM服务器的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有CM服务器的数量。 + */ + + int get_all_cmserver_num(); + + /* + 函数功能:获取所有CM代理的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有CM代理的数量。 + */ + + int get_all_cmagent_num(); + + /* + 函数功能:获取所有CNDN节点的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有主备节点的数量。 + */ + + int get_all_cndn_num(); + + /* + 函数功能:获取所有GTM节点的数量 + 函数参数:无 + 返回类型:int + 备注:该函数用于获取集群中所有GTM节点的数量。 + */ + + int get_all_gtm_num(); + + /* + 函数功能:根据值获取AZ属性 + 函数参数:value - 属性值 + data_dir - 数据目录 + 返回类型:char* + 备注:该函数根据给定的属性值和数据目录,返回对应的AZ属性值。 + */ + + char* get_AZ_value(const char* value, const char* data_dir); + + /* + 函数功能:根据节点名称获取AZ属性名称 + 函数参数:nodename - 节点名称 + 返回类型:char* + 备注:该函数根据给定的节点名称,返回对应的AZ属性名称。 + */ + + char* get_AZname_by_nodename(char* nodename); + + /* + 函数功能:将字符串转换为小写 + 函数参数:source - 源字符串 + dest - 目标字符串的地址 + destlen - 目标字符串的长度 + 返回类型:void + 备注:该函数将给定的源字符串转换为小写,并写入目标字符串。 + */ + + void make_string_tolower(const char* source, char* dest, const int destlen); + + /* + 函数功能:保存预期的实例信息 + 函数参数:datadir - 数据目录 + 返回类型:void + 备注:该函数用于保存预期的实例信息到指定的数据目录。 + */ + + void save_expect_instance_info(const char* datadir); + + /* + 函数功能:保存远程实例信息 + 函数参数:result_file - 结果文件路径 + nodename - 节点名称 + command - 命令 + guc_info - guc配置信息 + isRealGucInfo - 是否为真实的guc信息 + 返回类型:void + 备注:该函数用于保存远程实例信息,包括节点名称、命令以及guc配置信息。 + */ + + void save_remote_instance_info( + const char* result_file, const char* nodename, char* command, gucInfo* guc_info, bool isRealGucInfo); + + /* + 函数功能:执行本地实例操作 + 函数参数:type - 操作类型 + instance_name - 实例名称 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数根据给定的操作类型,执行本地实例的相关操作,包括启动、停止等。 + */ + + void do_local_instance(int type, char* instance_name, char* indatadir); + + /* + 函数功能:执行远程实例操作 + 函数参数:nodename - 节点名称 + instance_name - 实例名称 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数根据给定的节点名称和实例名称,执行远程实例的相关操作,包括启动、停止等。 + */ + + void do_remote_instance(char* nodename, const char* instance_name, const char* indatadir); + + /* + 函数功能:执行所有节点的实例操作 + 函数参数:instance_name - 实例名称 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数执行集群中所有节点的实例操作,包括启动、停止等。 + */ + + void do_all_nodes_instance(const char* instance_name, const char* indatadir); + + /* + 函数功能:检查环境变量的值 + 函数参数:input_env_value - 输入环境变量的值 + 返回类型:void + 备注:该函数用于检查给定的环境变量的值是否符合要求。 + */ + + void check_env_value(const char* input_env_value); + + /* + 函数功能:获取guc参数的类型 + 函数参数:type - 参数类型字符串 + 返回类型:GucParaType + 备注:该函数根据给定的guc参数类型字符串,返回相应的枚举值GucParaType。 + */ + + GucParaType get_guc_type(const char* type); + + /* + 函数功能:获取guc参数的单位 + 函数参数:unit - 单位字符串 + 返回类型:UnitType + 备注:该函数根据给定的单位字符串,返回相应的枚举值UnitType。 + */ + + UnitType get_guc_unit(const char* unit); + + /* + 函数功能:执行本地参数值的修改 + 函数参数:type - 参数类型 + datadir - 数据目录 + 返回类型:int + 备注:该函数根据给定的参数类型和数据目录,执行本地参数值的修改操作。 + */ + + int do_local_para_value_change(int type, char* datadir); + + /* + 函数功能:执行本地guc命令 + 函数参数:type - 命令类型 + temp_datadir - 临时数据目录 + 返回类型:int + 备注:该函数根据给定的命令类型和临时数据目录,执行本地的guc命令。 + */ + + int do_local_guc_command(int type, char* temp_datadir); + + /* + 函数功能:获取guc选项 + 函数参数:无 + 返回类型:char** + 备注:该函数用于获取guc命令的选项,并返回选项列表。 + */ + + char** get_guc_option(); + + /* + 函数功能:解析guc选项 + 函数参数:guc_opt - guc选项字符串 + guc_variable_list - guc配置枚举值列表 + 返回类型:int + 备注:该函数根据给定的guc选项字符串,解析出具体的guc配置枚举值。 + */ + + int do_gucopt_parse(const char* guc_opt, struct guc_config_enum_entry& guc_variable_list); + + /* + 函数功能:检查参数是否合法 + 函数参数:type - 参数类型 + 返回类型:int + 备注:该函数用于检查给定的参数类型是否合法。 + */ + + int check_parameter(int type); + + /* + 函数功能:检查参数值是否合法 + 函数参数:paraname - 参数名 + type - 参数类型 + guc_list_value - guc配置值列表 + guc_list_unit - guc配置单位列表 + value - 参数值 + 返回类型:int + 备注:该函数用于检查给定的参数值是否合法。 + */ + + int check_parameter_value( + const char* paraname, GucParaType type, char* guc_list_value, const char* guc_list_unit, const char* value); + + /* + 函数功能:检查参数名是否合法 + 函数参数:guc_opt - guc选项字符串列表 + type - 参数类型 + 返回类型:int + 备注:该函数用于检查给定的参数名是否合法。 + */ + + int check_parameter_name(char** guc_opt, int type); + + /* + 函数功能:检查参数是否有效 + 函数参数:type - 参数类型 + 返回类型:bool + 备注:该函数用于检查给定的参数类型是否有效。 + */ + + bool check_parameter_is_valid(int type); + + /* + 函数功能:解析参数值 + 函数参数:paraname - 参数名 + value - 参数值 + guc_list_unit - guc配置单位列表 + result_int - 输出整数值的地址 + result_double - 输出浮点数值的地址 + isInt - 是否为整数类型 + 返回类型:int + 备注:该函数根据给定的参数名、参数值和单位列表,解析出具体的数值。 + */ + + int parse_value(const char* paraname, const char* value, const char* guc_list_unit, int64* result_int, + double* result_double, bool isInt); + + /* + 函数功能:获取参数的最小最大值 + 函数参数:guc_list_val - guc配置值列表 + value_list - 最小最大值列表 + 返回类型:int + 备注:该函数根据给定的guc配置值列表,获取对应参数的最小最大值。 + */ + + int get_guc_minmax_value(const char* guc_list_val, struct guc_minmax_value& value_list); + + /* + 函数功能:检查整数或实数类型的参数值 + 函数参数:paraname - 参数名 + guc_list_value - guc配置值列表 + guc_list_unit - guc配置单位列表 + value - 参数值 + isInt - 是否为整数类型 + 返回类型:int + 备注:该函数用于检查给定的整数或实数类型的参数值是否合法。 + */ + + int check_int_real_type_value( + const char* paraname, const char* guc_list_value, const char* guc_list_unit, const char* value, bool isInt); + + /* + 函数功能:检查枚举类型的参数值 + 函数参数:paraname - 参数名 + guc_list_value - guc配置值列表 + value - 参数值 + 返回类型:int + 备注:该函数用于检查给定的枚举类型的参数值是否合法。 + */ + + int check_enum_type_value(const char* paraname, char* guc_list_value, const char* value); + + /* + 函数功能:检查布尔类型的参数值 + 函数参数:value - 参数值 + 返回类型:int + 备注:该函数用于检查给定的布尔类型的参数值是否合法。 + */ + + int check_bool_type_value(const char* value); + + /* + 函数功能:检查字符串类型的参数值 + 函数参数:paraname - 参数名 + value - 参数值 + 返回类型:int + 备注:该函数用于检查给定的字符串类型的参数值是否合法。 + */ + + int check_string_type_value(const char* paraname, const char* value); + + /* + 函数功能:针对CN/GTM节点执行命令 + 函数参数:type - 命令类型 + indatadir - 输入数据目录 + isCoordinator - 是否为协调节点 + 返回类型:void + 备注:该函数根据给定的命令类型、数据目录和节点类型,执行特定节点的命令。 + */ + + void do_command_for_cn_gtm(int type, char* indatadir, bool isCoordinator); + + /* + 函数功能:针对DN节点执行命令 + 函数参数:type - 命令类型 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数根据给定的命令类型和数据目录,执行数据节点的命令。 + */ + + void do_command_for_dn(int type, char* indatadir); + + /* + 函数功能:针对CM节点执行命令 + 函数参数:type - 命令类型 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数根据给定的命令类型和数据目录,执行CM服务器的命令。 + */ + + void do_command_for_cm(int type, char* indatadir); + + /* + 函数功能:针对CNDN节点执行命令 + 函数参数:type - 命令类型 + indatadir - 输入数据目录 + 返回类型:void + 备注:该函数根据给定的命令类型和数据目录,执行主备节点的命令。 + */ + + void do_command_for_cndn(int type, char* indatadir); + + /* + 函数功能:获取CM实际路径 + 函数参数:type - 节点类型 + 返回类型:char* + 备注:该函数根据给定的节点类型,返回对应CM的实际路径。 + */ + + char* get_cm_real_path(int type); + + /* + 函数功能:创建临时目录 + 函数参数:pathdir - 目录路径 + 返回类型:void + 备注:该函数用于在指定路径下创建临时目录。 + */ + + void create_tmp_dir(const char* pathdir); + + /* + 函数功能:删除临时目录 + 函数参数:pathdir - 目录路径 + 返回类型:void + 备注:该函数用于删除指定路径下的临时目录。 + */ + + void remove_tmp_dir(const char* pathdir); + + /* + 函数功能:检查记录 + 函数参数:type - 节点类型 + flag_str - 记录标识字符串 + 返回类型:bool + 备注:该函数用于检查给定的节点类型和记录标识字符串是否符合要求。 + */ + + bool is_record(int type, char* flag_str); + + /* + 函数功能:执行命令 + 函数参数:无 + 返回类型:bool + 备注:该函数用于执行命令,并返回执行结果。 + */ + + // 比较字符串函数 + // src_str - 源字符串 + // start_str - 起始字符串 + // end_str - 结束字符串 + bool compare_str(char* src_str, char* start_str, char* end_str); + + // 执行带实例名选项的本地命令 + // type - 类型 + // instance_name - 实例名 + void do_command_with_instance_name_option_local(int type, char* instance_name); + + // 在本地执行远程实例 + // nodename - 节点名 + // instance_name - 实例名 + // indatadir - 数据目录 + void do_remote_instance_local(char* nodename, const char* instance_name, const char* indatadir); + + // 在所有节点上执行本地实例 + // instance_name - 实例名 + // indatadir - 数据目录 + void do_all_nodes_instance_local(const char* instance_name, const char* indatadir); + + // 在所有节点上串行执行本地实例 + // instance_name - 实例名 + // indatadir - 数据目录 + void do_all_nodes_instance_local_in_serial(const char* instance_name, const char* indatadir); + + // 在所有节点上并行执行本地实例 + // instance_name - 实例名 + // indatadir - 数据目录 + void do_all_nodes_instance_local_in_parallel(const char* instance_name, const char* indatadir); + + // 获取GUC行信息 + // line - 行 + // 返回GUC行信息数组 + char** get_guc_line_info(const char** line); + + // 获取环境变量字符串 + // env - 环境变量 + // 返回环境变量字符串 + static char* GetEnvStr(const char* env); + + // 并行执行命令 + // cmd - 命令 + // idx - 索引 + // is_local_node - 是否为本地节点 + static void executePopenCommandsParallel(const char* cmd, int idx, bool is_local_node); + + // 并行读取输出 + // cmd - 命令 + // if_for_all_instance - 是否为所有实例 + static void readPopenOutputParallel(const char* cmd, bool if_for_all_instance); + + // 以毫秒为单位的休眠 + // sleepMs - 休眠时间(毫秒) + static void SleepInMilliSec(uint32_t sleepMs); + + // 初始化全局命令 + static void init_global_command(); + + // 重置全局命令 + static void reset_global_command(); + + // 在所有节点上并行执行本地实例循环 + // instance_name - 实例名 + // indatadir - 数据目录 + static void do_all_nodes_instance_local_in_parallel_loop(const char* instance_name, const char* indatadir); + + /******************************************************************************* + 函数:xstrdup + 描述:复制字符串并分配新的内存空间 + 输入:s - 源字符串 + 输出:无 + 返回:目标字符串 + ***************************************************************************** + */ + char* xstrdup(const char* s) + { + char* result = NULL; + + result = strdup(s); + if (NULL == result) { + (void)write_stderr(_("%s: out of memory\n"), "gs_guc"); + exit(1); + } + return result; + } + /* + ****************************************************************************** + 函数:make_string_tolower + 描述:复制源字符串到目标字符串,并将目标字符串中的所有字母转换为小写 + 输入:source - 源字符串 + dest - 目标字符串 + destlen - 目标字符串长度 + 输出:无 + 返回:无 + ***************************************************************************** + */ + void make_string_tolower(const char* source, char* dest, const int destlen) + { + int i = 0; + int len = (int)strlen(source); + if (len > destlen) { + len = destlen; + } + for (i = 0; i < len; i++) + dest[i] = tolower(source[i]); + dest[i] = '\0'; + } + /* + ****************************************************************************** + 函数:get_instance_type + 描述:获取实例类型 + 输入:无 + 输出:无 + 返回:实例类型 + ****************************************************************************** + */ + const char* get_instance_type() + { + char* type = NULL; + switch (nodetype) { + case INSTANCE_COORDINATOR: { + type = "-Z coordinator"; + break; + } + case INSTANCE_DATANODE: { +#ifdef ENABLE_MULTIPLE_NODES + type = "-Z datanode"; +#else + type = ""; +#endif + break; + } + case INSTANCE_CMSERVER: { + type = "-Z cmserver"; + break; + } + case INSTANCE_CMAGENT: { + type = "-Z cmagent"; + break; + } + case INSTANCE_GTM: { + type = "-Z gtm"; + break; + } + default: { + type = ""; + break; + } + } + return (const char*)type; +} +``` +/* + ****************************************************************************** + Function : modify_parameter_value + Description : 如果参数值包含特殊字符 "$",在远程设置时需要先修改参数值。 + 输入 : value 参数值 + : localMode 在本地节点上执行操作 + Return : char * + Warning : 此函数会为返回值分配内存,但在此函数中不会释放此内存。 + 所以调用者在使用此函数的返回值后应该释放这段内存。 +//该代码是一个用于修改参数值的函数。函数的目的是在参数值中查找特殊字符 "$",并根据是否在本地节点上执行操作,进行相应的修改。 +// +//函数中的变量说明: +//- `value`:参数值 +//- `localMode`:是否在本地节点上执行操作的标志,类型为布尔型 +//- `i`、`j`、`k`:循环计数变量 +//- `backslash_num`:反斜杠的数量,用于添加到特殊字符 "$" 前 +//- `local_backslash_num`:在本地模式下需要添加的反斜杠数量 +//- `remote_backslash_num`:在远程模式下需要添加的反斜杠数量 +//- `buffer`:存储修改后的参数值的缓冲区 +// +//函数的实现逻辑如下: +//1. 分配大小为 `MAX_VALUE_LEN` 的字符缓冲区 `buffer`。 +//2. 遍历参数值 `value` 中的每个字符: +//- 如果字符是特殊字符 "$",则根据 `localMode` 的值确定需要添加的反斜杠数量,将相应数量的反斜杠添加到 `buffer` 中,并将特殊字符 "$" 添加到 `buffer` 中。 +//- 否则,将当前字符直接添加到 `buffer` 中。 +//3. 返回修改后的参数值 `buffer`。 +// +//示例应用: +//假设有一个配置文件中的参数值为 `"$libdir/xxx"`,在本地模式下需要修改为 `"\$libdir / xxx"`,在远程模式下需要修改为 `"\\\$libdir / xxx"`,可以使用该函数实现。调用函数时,传入参数值和模式标志,即可得到修改后的参数值。 + ****************************************************************************** +*/ + +// 修改参数值的函数 +char* modify_parameter_value(const char* value, bool localMode) +{ + int i = 0; + int j = 0; + int k = 0; + int backslash_num = 0; + const int local_backslash_num = 1; + const int remote_backslash_num = 3; + + // 分配内存空间 + char* buffer = (char*)pg_malloc_zero(MAX_VALUE_LEN * sizeof(char)); + + // 遍历参数值,修改特殊字符 + for (i = 0, j = 0; i < (int)strlen(value) && j < MAX_VALUE_LEN; i++, j++) { + if (value[i] == '$') { + /* + * 如果参数值包含特殊字符 "$",在本地命令和远程命令中在 "$" 前添加反斜杠的数量不同。 + * 在远程设置时,命令如下: + * remote command: ssh -n nodename "gs_guc set -Z datanode -I all -c \"dynamic_library_path='\\\$libdir/xxx'\"" + * 在本地执行时,命令如下: + * local command: gs_guc set -Z datanode -I all -c \"dynamic_library_path='\$libdir/xxx'\" + */ + backslash_num = localMode ? local_backslash_num : remote_backslash_num; + for (k = 0; k < backslash_num && j < MAX_VALUE_LEN; k++) { + buffer[j] = '\\'; + j++; + } + if (j >= MAX_VALUE_LEN) { + write_stderr(_("%s: out of memory\n"), progname); + exit(1); + } + buffer[j] = value[i]; + } + else { + buffer[j] = value[i]; + } + } + return buffer; +} + +/* + ****************************************************************************** + Function : form_commandline_options + Description : 生成完整的guc命令 + Input : instance_name - 实例名称 + indatadir - 实例路径 + local_mode - 是否是本地模式 + Output : 无 + Return : 无 + ****************************************************************************** +*/ +static char* form_commandline_options(const char* instance_name, const char* indatadir, bool local_mode) +{ + char* buffer = NULL; // 存储完整命令的缓冲区 + int buflen = 0; // 缓冲区长度 + int curlen = 0; // 当前长度 + int i = 0; + int nRet = 0; + char* new_value = NULL; // 存储新参数值的变量 + + /* 其他标准选项 */ +#define MIN_COMMAND_LEN 256 + + /* 添加 -c + '=' + 两个空格 + 两个反斜杠 + 两个引号 */ +#define ALLIG_POSTGRES_CONF_LEN 20 + + /* 添加 -h + 两个反斜杠 + 两个引号 */ +#define ALLIG_HBA_CONF_LEN 10 + + buflen = MIN_COMMAND_LEN; // 初始化缓冲区长度为最小命令长度 + if (instance_name != NULL) { + buflen += strlen(instance_name); // 如果实例名称存在,添加实例名称长度 + } + else { + buflen += strlen(indatadir); // 如果实例名称不存在,添加实例路径长度 + } + + /* 计算选项所需的长度 */ + for (i = 0; i < config_param_number; i++) { + if (!is_hba_conf) { + buflen += (ALLIG_POSTGRES_CONF_LEN + strlen(config_param[i])); // 不是hba配置文件时,添加参数长度 + if (config_value[i] != NULL) { + buflen += strlen(config_value[i]); // 如果参数值存在,添加参数值长度 + } + } + else { + buflen += ALLIG_HBA_CONF_LEN; // 是hba配置文件时,只添加固定长度 + if (config_value[i] != NULL) { + buflen += strlen(config_value[i]); // 如果参数值存在,添加参数值长度 + } + } + } + + buffer = (char*)pg_malloc_zero(buflen); // 分配缓冲区内存 + + /* 设置/重置 [--cordinator --datanode --gtm ] */ + curlen = snprintf_s( + buffer, buflen, buflen - 1, "gs_guc %s %s ", get_ctl_command_type(), get_instance_type()); + securec_check_ss_c(curlen, buffer, "\0"); + + // 示例:get_ctl_command_type()返回"set",get_instance_type()返回"cordinator",则buffer为"gs_guc set cordinator " + + if (nodetype == INSTANCE_CMAGENT || + nodetype == INSTANCE_CMSERVER) { + nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), "-D \"cm_instance_data_path\""); + } + else { + /* -I or -D */ + if (NULL != instance_name) { + nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), "-I %s", + instance_name); + } + else { + nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), "-D \"%s\"", + indatadir); + } + } + securec_check_ss_c(nRet, buffer, "\0"); + curlen = curlen + nRet; + + // 示例:如果nodetype等于INSTANCE_CMAGENT,那么加上"-D \"cm_instance_data_path\"",否则如果实例名称存在,加上"-I 实例名称",否则加上"-D 实例路径" + + /* -c 选项 */ + for (i = 0; i < config_param_number; i++) { + if (!is_hba_conf) { + if (config_value[i] != NULL) { + new_value = modify_parameter_value(config_value[i], local_mode); + if (local_mode) { + nRet = snprintf_s(buffer + curlen, + (buflen - curlen), + (buflen - curlen - 1), + " -c %c%s=%s%c", + '"', + config_param[i], + new_value, + '"'); + } + else { + nRet = snprintf_s(buffer + curlen, + (buflen - curlen), + (buflen - curlen - 1), + " -c \\\"%s=%s\\\"", + config_param[i], + new_value); + } + GS_FREE(new_value); + } + else { + nRet = snprintf_s(buffer + curlen, (buflen - curlen), (buflen - curlen - 1), " -c %s", config_param[i]); + } + securec_check_ss_c(nRet, buffer, "\0"); + curlen = curlen + nRet; + } + else { + if (local_mode) { + nRet = snprintf_s( + buffer + curlen, (buflen - curlen), (buflen - curlen - 1), " -h %c%s%c", '"', config_value[i], '"'); + } + else { + nRet = snprintf_s( + buffer + curlen, (buflen - curlen), (buflen - curlen - 1), " -h \\\"%s\\\"", config_value[i]); + } + securec_check_ss_c(nRet, buffer, "\0"); + curlen = curlen + nRet; + } + } + + return buffer; +} +``` +/* + ****************************************************************************** + Function : get_nodeidx_by_HA + Description : 根据HA的IP地址和端口获取节点索引 + Input : HAIp - HA的IP地址 + : HAPort - HA的端口 + Output : None + Return : int - 节点索引 + ******************************************************************************* +*/ +uint32 get_nodeidx_by_HA(const char* HAIp, uint32 HAPort) +{ + uint32 i = 0; + uint32 j = 0; + + // 遍历所有节点 + for (i = 0; i < g_node_num; i++) { + // 遍历当前节点的所有数据节点 + for (j = 0; j < g_node[i].datanodeCount; j++) { + // 通过比较IP地址和端口号找到匹配的节点 + if ((0 == strncmp(g_node[i].datanode[j].datanodeLocalHAIP[0], HAIp, strlen(HAIp))) && + (0 == g_node[i].datanode[j].datanodeLocalHAPort - HAPort)) + return i; // 返回节点索引 + } + } + return 0; // 没有找到匹配的节点,返回0 +} + +/* + ****************************************************************************** + Function : get_instance_id + Description : 根据数据路径和HA的IP地址和端口获取实例ID + Input : dataPath - 数据节点实例路径 + : HAIp - HA的IP地址 + : HAPort - HA的端口 + Output : None + Return : int - 数据节点实例ID + ****************************************************************************** +*/ +uint32 get_instance_id(const char* dataPath, const char* HAIp, uint32 HAPort) +{ + uint32 i = 0; + uint32 nodeidx = 0; + + // 先根据HA的IP地址和端口获取节点索引 + nodeidx = get_nodeidx_by_HA(HAIp, HAPort); + // 遍历指定节点的所有数据节点 + for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { + // 通过比较数据路径找到匹配的实例ID + if (0 == strncmp(g_node[nodeidx].datanode[i].datanodeLocalDataPath, dataPath, strlen(dataPath))) + return g_node[nodeidx].datanode[i].datanodeId; // 返回实例ID + } + return 0; // 没有找到匹配的实例ID,返回0 +} + +/* + ****************************************************************************** + Function : is_instance_level_correct + Description : 检查实例的级别是否正确,通过数据路径、HA的IP地址和端口以及级别进行检查 + Input : dataPath - 数据节点实例路径 + : HAIp - HA的IP地址 + : HAPort - HA的端口 + : level - 实例级别 + Output : None + Return : True/False + ****************************************************************************** +*/ +bool is_instance_level_correct(const char* dataPath, const char* HAIp, uint32 HAPort, uint32 level) +{ + uint32 i = 0; + uint32 nodeidx = 0; + + // 先根据HA的IP地址和端口获取节点索引 + nodeidx = get_nodeidx_by_HA(HAIp, HAPort); + // 遍历指定节点的所有数据节点 + for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { + // 通过比较数据路径和级别进行匹配 + if ((0 == strncmp(g_node[nodeidx].datanode[i].datanodeLocalDataPath, dataPath, strlen(dataPath))) && + (level == g_node[nodeidx].datanode[i].datanodeRole)) + return true; // 匹配成功,返回true + } + return false; // 没有匹配的实例或级别,返回false +} + +/* + ****************************************************************************** +GetPgxcNodeNameForMasterDnInstance + get the pgxc_node_name on single primary mutile standby cluster, by node index and datanode instance index +Input: nodeidx -> node index(it comes from static_config_file) + instanceidx -> node index(it comes from static_config_file) + ****************************************************************************** +*/ +char* GetPgxcNodeNameForMasterDnInstance(int32 nodeidx, int32 instanceidx) +{ + char* pgxcNodeName = (char*)pg_malloc_zero(sizeof(char) * MAXPGPATH); + int ret = 0; + + /* + * get all standby dn instance id arry. + * name dn_6001_6002_6003 -> 6001 must be primary DN instance + */ + uint32 instance_id_arry[CM_NODE_MAXNUM] = {0}; + + ret = snprintf_s(pgxcNodeName, MAXPGPATH, MAXPGPATH - 1, "dn_%u", g_node[nodeidx].datanode[instanceidx].datanodeId); + securec_check_ss_c(ret, "\0", "\0"); + + if (g_dn_replication_num == 0) { + write_stderr("ERROR: Failed to get dn instance in the partition.\n"); + exit(1); + } + + for (uint32 dnId = 0; dnId < g_dn_replication_num - 1; dnId++) { + char tmp_command[MAXPGPATH] = {0}; + /* + * The instance must be standby. So use datanodePeerHAIP replace datanodePeer2HAIP + */ + instance_id_arry[dnId] = + get_instance_id(g_node[nodeidx].datanode[instanceidx].peerDatanodes[dnId].datanodePeerDataPath, + g_node[nodeidx].datanode[instanceidx].peerDatanodes[dnId].datanodePeerHAIP[0], + g_node[nodeidx].datanode[instanceidx].peerDatanodes[dnId].datanodePeerHAPort); + ret = memset_s(tmp_command, MAXPGPATH, '\0', MAXPGPATH); + securec_check_c(ret, "\0", "\0"); + ret = snprintf_s(tmp_command, MAXPGPATH, MAXPGPATH - 1, "_%u", instance_id_arry[dnId]); + securec_check_ss_c(ret, "\0", "\0"); + ret = strncat_s(pgxcNodeName, MAXPGPATH, tmp_command, strlen(tmp_command)); + securec_check_ss_c(ret, "\0", "\0"); + } + + return pgxcNodeName; +} +/* + ****************************************************************************** + CheckInstanceNameForSinglePrimaryMutilStandby + 检查实例名称是否属于单主多备集群 + 实例类型信息:与OM一致 + PRIMARY_DN 0 + STANDBY_DN 1 + DUMMY_STANDBY_DN 2 + ****************************************************************************** +*/ +bool CheckInstanceNameForSinglePrimaryMutilStandby(int32 nodeidx, const char* instance_name) +{ + uint32 i = 0; + uint32 j = 0; + int ret = 0; + uint32 nameLen = 0; + char* pgxcNodeName = NULL; + + for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { + /* 处理主节点实例分支 */ + if (g_node[nodeidx].datanode[i].datanodeRole == 0) { + pgxcNodeName = GetPgxcNodeNameForMasterDnInstance(nodeidx, i); + nameLen = strlen(instance_name) > strlen(pgxcNodeName) ? strlen(instance_name) : strlen(pgxcNodeName); + ret = strncmp(pgxcNodeName, instance_name, nameLen); + GS_FREE(pgxcNodeName); + if (ret == 0) { + return true; + } + } + else { + /* 处理备节点实例分支 + * 主节点和备节点的pgxc_node_name相同,因此通过主节点实例获取 + * 首先获取主节点的索引、实例索引和数据路径 + */ + uint32 nodeIndex = 0; + uint32 instanceidx = 0; + char dataPath[MAXPGPATH] = { 0 }; + size_t dataPathLen = 0; + + ret = memset_s(dataPath, MAXPGPATH, '\0', MAXPGPATH); + securec_check_c(ret, "\0", "\0"); + + /* + * 每个数据环必须有一个主实例,获取节点索引和数据路径 + */ + for (uint32 dnId = 0; dnId < g_dn_replication_num - 1; dnId++) { + if (0 == g_node[nodeidx].datanode[i].peerDatanodes[dnId].datanodePeerRole) { + nodeIndex = get_nodeidx_by_HA(g_node[nodeidx].datanode[i].peerDatanodes[dnId].datanodePeerHAIP[0], + g_node[nodeidx].datanode[i].peerDatanodes[dnId].datanodePeerHAPort); + ret = snprintf_s(dataPath, + MAXPGPATH, + MAXPGPATH - 1, + "%s", + g_node[nodeidx].datanode[i].peerDatanodes[dnId].datanodePeerDataPath); + securec_check_ss_c(ret, "\0", "\0"); + break; + } + } + + /* + * 检查结果,确保找到主实例的节点索引和实例数据路径 + */ + if (dataPath[0] == '\0') { + fprintf(stderr, _("ERROR: Failed to get primary DN instance information.\n")); + exit(1); + } + + /* + * 通过节点索引和数据路径获取主节点实例索引 + */ + for (j = 0; j < g_node[nodeIndex].datanodeCount; j++) { + if (g_node[nodeIndex].datanode[i].datanodeRole == 0) { + dataPathLen = strlen(g_node[nodeIndex].datanode[j].datanodeLocalDataPath); + if (0 == strncmp(g_node[nodeIndex].datanode[j].datanodeLocalDataPath, + dataPath, + dataPathLen > strlen(dataPath) ? dataPathLen : strlen(dataPath))) { + instanceidx = j; + break; + } + } + } + + pgxcNodeName = GetPgxcNodeNameForMasterDnInstance(nodeIndex, instanceidx); + nameLen = strlen(instance_name) > strlen(pgxcNodeName) ? strlen(instance_name) : strlen(pgxcNodeName); + ret = strncmp(pgxcNodeName, instance_name, nameLen); + GS_FREE(pgxcNodeName); + if (0 == ret) { + return true; + } + } + } + return false; +} +/** + ****************************************************************************** + Function : validate_instance_name_for_DN + Description : 验证DN实例名称的有效性。 + Input : + nodeidx - 节点id索引 + instance_name - 实例名称 + Return : bool + ****************************************************************************** +*/ +bool validate_instance_name_for_DN(int32 nodeidx, const char* instance_name) +{ + bool isCorrect = false; // 标记实例名称是否正确 + uint32 i = 0; // 循环计数器 + uint32 instance_id = 0; // 实例ID + char temp_instance_name[MAXPGPATH]; // 临时实例名称 + int rc = 0; // 函数调用返回值 + + rc = memset_s(temp_instance_name, MAXPGPATH, '\0', MAXPGPATH); // 初始化temp_instance_name + + if ((int)strlen(instance_name) < DN_INSTANCE_LEN) { // 实例名称长度不合法 + return false; // 返回false,表示实例名称不正确 + } + + /*single primary multil standby */ + if (g_multi_az_cluster) { // 多AZ集群 + isCorrect = CheckInstanceNameForSinglePrimaryMutilStandby(nodeidx, instance_name); // 调用检查实例名称的函数 + } + else { + /* master_standby */ + if ((int)strlen(instance_name) != DN_INSTANCE_LEN) // 实例名称长度不合法 + return false; // 返回false,表示实例名称不正确 + + for (i = 0; i < g_node[nodeidx].datanodeCount; i++) { // 遍历所有DataNode + if (g_node[nodeidx].datanode[i].datanodeRole == 0) { // 主节点 + if (is_instance_level_correct(g_node[nodeidx].datanode[i].datanodePeerDataPath, + g_node[nodeidx].datanode[i].datanodePeerHAIP[0], + g_node[nodeidx].datanode[i].datanodePeerHAPort, + 1)) // 调用检查实例级别的函数,返回是否正确 + instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeerDataPath, + g_node[nodeidx].datanode[i].datanodePeerHAIP[0], + g_node[nodeidx].datanode[i].datanodePeerHAPort); // 获取实例ID + else + instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeer2DataPath, + g_node[nodeidx].datanode[i].datanodePeer2HAIP[0], + g_node[nodeidx].datanode[i].datanodePeer2HAPort); // 获取实例ID + rc = snprintf_s(temp_instance_name, + MAXPGPATH, + MAXPGPATH - 1, + "dn_%d_%d", + (int)g_node[nodeidx].datanode[i].datanodeId, + (int)instance_id); // 根据实例ID生成临时实例名称 + } + else { // 备节点 + if (is_instance_level_correct(g_node[nodeidx].datanode[i].datanodePeerDataPath, + g_node[nodeidx].datanode[i].datanodePeerHAIP[0], + g_node[nodeidx].datanode[i].datanodePeerHAPort, + 0)) // 调用检查实例级别的函数,返回是否正确 + instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeerDataPath, + g_node[nodeidx].datanode[i].datanodePeerHAIP[0], + g_node[nodeidx].datanode[i].datanodePeerHAPort); // 获取实例ID + else + instance_id = get_instance_id(g_node[nodeidx].datanode[i].datanodePeer2DataPath, + g_node[nodeidx].datanode[i].datanodePeer2HAIP[0], + g_node[nodeidx].datanode[i].datanodePeer2HAPort); // 获取实例ID + rc = snprintf_s(temp_instance_name, + MAXPGPATH, + MAXPGPATH - 1, + "dn_%d_%d", + (int)instance_id, + (int)g_node[nodeidx].datanode[i].datanodeId); // 根据实例ID生成临时实例名称 + } + securec_check_ss_c(rc, "\0", "\0"); // 检查snprintf_s函数调用的返回值 + + if (strncmp(temp_instance_name, instance_name, strlen(instance_name)) == 0) { // 比较实例名称是否匹配 + isCorrect = true; // 实例名称正确 + break; // 结束循环 + } + + rc = memset_s(temp_instance_name, MAXPGPATH, '\0', MAXPGPATH); // 初始化temp_instance_name + } + } + + return isCorrect; // 返回实例名称是否正确的结果 +} +/* + ****************************************************************************** + Function : validate_remote_instance_name + Description : 验证远程实例名称。 + 类似于gs_guc命令的格式:"-I 实例名称 -N 节点名称"。 + 实例名称类型如下: + INSTANCE_COORDINATOR -> cn_instanceId + INSTANCE_GTM -> one + INSTANCE_DATANODE -> dn_masterId_slaveId, dn_masterId_dummyslaveId + Input : nodename - 节点名称 + type - 实例类型 + instance_name - 实例名称 + Return : int + ****************************************************************************** +*/ +int validate_remote_instance_name(char* nodename, int type, char* instance_name) +{ + int32 nodeidx = 0; + char temp_instance_name[MAXPGPATH]; + int rc = 0; + bool isCorrect = false; + + rc = memset_s(temp_instance_name, MAXPGPATH, '\0', MAXPGPATH); + securec_check_c(rc, "\0", "\0"); + + nodeidx = get_nodeidx_by_name(nodename); + /* 检查节点名称,确保其在集群静态配置文件中存在 */ + if (nodeidx < 0) { + write_stderr("ERROR: 节点 %s 在静态配置文件中未找到。\n", nodename); + return 1; + } + + /* + * INSTANCE_COORDINATOR -> cn_instanceId + * INSTANCE_GTM -> one + * INSTANCE_DATANODE -> dn_masterId_slaveId, dn_masterId_dummyslaveId + */ + if (type == INSTANCE_COORDINATOR) { + rc = snprintf_s(temp_instance_name, MAXPGPATH, MAXPGPATH - 1, "cn_%d", (int)g_node[nodeidx].coordinateId); + securec_check_ss_c(rc, "\0", "\0"); + + if ((CN_INSTANCE_LEN == (int)strlen(instance_name)) && + (0 == strncmp(temp_instance_name, instance_name, strlen(instance_name)))) + isCorrect = true; + } + else if (type == INSTANCE_GTM) { + if ((0 != g_node[nodeidx].gtmId) && (GTM_INSTANCE_LEN == (int)strlen(instance_name)) && + (0 == strncmp(instance_name, "one", strlen("one")))) + isCorrect = true; + } + else { + isCorrect = validate_instance_name_for_DN(nodeidx, instance_name); + } + + if (isCorrect) { + return 0; + } + else { + write_stderr("ERROR: 实例名称 %s 不正确。\n", instance_name); + return 1; + } +} +/* + ****************************************************************************** + Function : validate_nodename + Description : 验证节点名称,如果节点名称不是 all,则确保其在集群静态配置文件中存在。 + Input : nodename - 节点名称 + Return : int + ****************************************************************************** +*/ +int validate_nodename(char* nodename) +{ + int32 nodeidx = 0; + /* 确保节点名称正确 */ + if ((NULL != nodename) && (0 != strncmp(nodename, "all", sizeof("all")))) { + nodeidx = get_nodeidx_by_name(nodename); + /* 检查节点名称,确保其在集群静态配置文件中存在 */ + if (nodeidx < 0) { + write_stderr("ERROR: 节点 %s 在静态配置文件中未找到。\n", nodename); + return 1; + } + } + return 0; +} +/* + ****************************************************************************** + 函数:check_instance_name + 描述:检查实例名称。我们知道节点名称和实例名称都不为'NULL'和'all'。 + 输入:nodename - 节点名称 + type - 实例类型 + instance_name - 实例名称 + 返回:int + ****************************************************************************** +*/ +int check_instance_name(char* nodename, int type, char* instance_name) +{ + char temp_datadir[MAXPGPATH]; + int rc = 0; + + rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); + securec_check_c(rc, "\0", "\0"); + + /* -N nodename -I instance_name */ + if ((0 != strncmp(nodename, "all", sizeof("all"))) && (0 != strncmp(instance_name, "all", sizeof("all")))) { + if (is_local_node(nodename)) { + if (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_ERROR) { + write_stderr("ERROR: 实例名称 %s 不正确。\n", instance_name); + return 1; + } + } + else { + if (0 != validate_remote_instance_name(nodename, type, instance_name)) + return 1; + } + } + + return 0; +} + +/* + ****************************************************************************** + 函数:validate_node_instance_name + 描述:验证节点名称和实例名称 + 输入:nodename - 节点名称 + type - 实例类型 + instance_name - 实例名称 + 输出:无 + 返回:int + ****************************************************************************** +*/ +int validate_node_instance_name(char* nodename, int type, char* instance_name) +{ + char temp_datadir[MAXPGPATH]; + int rc = 0; + + rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); + securec_check_c(rc, "\0", "\0"); + + /* 验证节点名称是否正确 */ + if (0 != validate_nodename(nodename)) + return 1; + + if ((NULL == nodename) && (NULL != instance_name)) { + /* -I instance_name */ + if ((0 != strncmp(instance_name, "all", sizeof("all"))) && + (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_ERROR)) { + write_stderr("ERROR: 实例名称 %s 不正确。\n", instance_name); + return 1; + } + } + + if ((NULL != nodename) && (NULL != instance_name)) { + /* 跳过检查 '-N all -I all', '-N nodename -I all' */ + /* 对于非数据节点(INSTANCE_DATANODE),命令 '-N all -I instance_name' 是不正确的 */ + if (type != INSTANCE_DATANODE) { + if ((strncmp(nodename, "all", sizeof("all")) == 0) && (strncmp(instance_name, "all", sizeof("all")) != 0)) { + write_stderr("ERROR: 实例名称 %s 不正确。当 -N 为 'all' 时,-I 必须相同。\n", instance_name); + return 1; + } + } + + /* -N nodename -I instance_name */ + if (0 != check_instance_name(nodename, type, instance_name)) + return 1; + } + + return 0; +} + +/* + ****************************************************************************** + 函数:validate_cluster_guc_options + 描述:检查 -N、-I 和 -D 参数 + 输入:nodename - 节点名称 + type - 节点类型 + instance_name - 实例名称 + indatadir - 实例数据目录 + 输出:无 + 返回:int + ****************************************************************************** +*/ +int validate_cluster_guc_options(char* nodename, int type, char* instance_name, char* indatadir) +{ + if ((NULL != nodename) || (NULL != instance_name)) { + if (0 != init_gauss_cluster_config()) { + (void)write_stderr("ERROR: 无法从静态配置文件中获取集群信息。\n"); + return 1; + } + } + + if ((NULL == instance_name) && (NULL == indatadir)) { + if (type == INSTANCE_CMAGENT || type == INSTANCE_CMSERVER) { + write_stderr("ERROR: 执行 gs_guc 时需要 -I all。\n"); + } + else { + write_stderr("ERROR: 执行 gs_guc 时需要 -D 或者 -I。\n"); + } + return 1; + } + else if ((NULL != instance_name) && (NULL != indatadir)) { + write_stderr("ERROR: 执行 gs_guc 时只需要 -D 或者 -I 其中之一。\n"); + return 1; + } + + if (node_type_number == LARGE_INSTANCE_NUM && (NULL != instance_name) && + (0 != strncmp(instance_name, "all", sizeof("all")))) { + write_stderr("ERROR: 当 -Z 同时为 coordinator 和 datanode 时,-I 必须为 'all'。\n"); + return 1; + } + + /* 用户保证 -D 参数值的正确性 */ + if (0 != validate_node_instance_name(nodename, type, instance_name)) + return 1; + + do_checkvalidate(type); + + return 0; +} +*/ + +// 示例说明: +// check_instance_name 函数用于检查实例名称,并根据节点名称和实例名称判断是否进行进一步的验证。如果节点是本地节点,则根据实例名称获取本地数据目录,并进行验证;如果节点是远程节点,则调用 validate_remote_instance_name 函数进行验证。该函数可以用于验证命令行参数中的节点名称和实例名称的正确性。 + +// validate_node_instance_name 函数用于验证节点名称和实例名称的正确性。首先,通过 validate_nodename 函数验证节点名称是否正确。然后,根据命令行参数中的节点名称和实例名称判断是否进行进一步的验证。如果节点名称为 NULL 而实例名称不为 NULL,则根据实例名称获取本地数据目录,并进行验证。如果节点名称和实例名称都不为 NULL,则判断是否为特定情况下的非法输入,并调用 check_instance_name 函数进行验证。该函数可以用于验证命令行参数中的节点名称和实例名称的正确性。 + +// validate_cluster_guc_options 函数用于检查 -N、-I 和 -D 参数的正确性。首先,根据命令行参数中的节点名称和实例名称判断是否需要从静态配置文件中获取集群信息。然后,根据参数的不同情况判断是否为非法输入,并调用 validate_node_instance_name 函数进行验证。最后,根据节点类型调用 do_checkvalidate 函数进行进一步的检查。该函数可以用于验证命令行参数中的 -N、-I 和 -D 参数的正确性。 +/* + ****************************************************************************** +函数:save_expect_instance_info +描述:保存期望的实例信息到全局参数中。 + 节点名称和实例配置文件路径。 +输入:datadir(实例的目录) +输出:"expected instance path: %s\n",gucconf_file +返回值:void +****************************************************************************** +*/ +void save_expect_instance_info(const char* datadir) +{ + int i = 0; + if (NULL == datadir || '\0' == datadir[0]) { + (void)write_stderr("instance data directory is NULL.\n"); + return; + } + + // 获取配置文件,如pg_hba.conf/postgresql.conf/cmagent.conf + get_instance_configfile(datadir); + if (CHECK_CONF_COMMAND == ctl_command) { + for (i = 0; i < config_param_number; i++) { + g_expect_gucInfo->nodename_array[g_expect_gucInfo->nodename_num++] = xstrdup(g_local_node_name); + g_expect_gucInfo->gucinfo_array[g_expect_gucInfo->gucinfo_num++] = xstrdup(gucconf_file); + g_expect_gucInfo->paramname_array[g_expect_gucInfo->paramname_num++] = xstrdup(config_param[i]); + g_expect_gucInfo->paramvalue_array[g_expect_gucInfo->paramvalue_num++] = xstrdup("NULL"); + (void)write_stderr( + "expected guc information: %s: %s=NULL: [%s]\n", g_local_node_name, config_param[i], gucconf_file); + } + } + else { + g_expect_gucInfo->nodename_array[g_expect_gucInfo->nodename_num++] = xstrdup(g_local_node_name); + g_expect_gucInfo->gucinfo_array[g_expect_gucInfo->gucinfo_num++] = xstrdup(gucconf_file); + (void)write_stderr("expected instance path: [%s]\n", gucconf_file); + } +} + +/* + ****************************************************************************** +函数:check_env_value +描述:检查环境变量的值是否合法。 +输入:input_env_value +输出:无 +返回值:void +****************************************************************************** +*/ +void check_env_value(const char* input_env_value) +{ + const char* danger_character_list[] = { "|", ";", "&", "$", "<", ">", "`", "\\", "'", "\"", "{", "}", "(", ")", "[", "]", "~", "*", "?", "!", "\n", NULL }; + int i = 0; + + for (i = 0; danger_character_list[i] != NULL; i++) { + if (strstr(input_env_value, danger_character_list[i]) != NULL) { + fprintf(stderr, _("ERROR: Failed to check environment value: invalid token \"%s\".\n"), danger_character_list[i]); + exit(1); + } + } +} + +/* + ****************************************************************************** +函数:get_env_value +描述:获取环境变量的值。 +输入:env_var(环境变量名),output_env_value(输出环境变量值) +输出:无 +返回值:bool +****************************************************************************** +*/ +bool get_env_value(const char* env_var, char* output_env_value) +{ + // 省略函数实现,示例代码中未给出该函数 + return false; +} +*/ + +/* + 对上述代码进行注释后的结果为: + /* + ****************************************************************************** + 函数:save_expect_instance_info + 描述:保存期望的实例信息到全局参数中。 + 节点名称和实例配置文件路径。 + 输入:datadir(实例的目录) + 输出:"expected instance path: %s\n",gucconf_file + 返回值:void + ****************************************************************************** + */ + // 将期望的实例信息保存到全局参数中 + void save_expect_instance_info(const char* datadir) +{ + int i = 0; + if (NULL == datadir || '\0' == datadir[0]) { + (void)write_stderr("instance data directory is NULL.\n"); + return; + } + + // 获取配置文件,如pg_hba.conf/postgresql.conf/cmagent.conf + get_instance_configfile(datadir); + if (CHECK_CONF_COMMAND == ctl_command) { + for (i = 0; i < config_param_number; i++) { + // 保存期望的节点名称、实例配置文件路径、配置参数名称和参数值到全局参数中 + g_expect_gucInfo->nodename_array[g_expect_gucInfo->nodename_num++] = xstrdup(g_local_node_name); + g_expect_gucInfo->gucinfo_array[g_expect_gucInfo->gucinfo_num++] = xstrdup(gucconf_file); + g_expect_gucInfo->paramname_array[g_expect_gucInfo->paramname_num++] = xstrdup(config_param[i]); + g_expect_gucInfo->paramvalue_array[g_expect_gucInfo->paramvalue_num++] = xstrdup("NULL"); + (void)write_stderr( + "expected guc information: %s: %s=NULL: [%s]\n", g_local_node_name, config_param[i], gucconf_file); + } + } + else { + // 保存期望的节点名称和实例配置文件路径到全局参数中 + g_expect_gucInfo->nodename_array[g_expect_gucInfo->nodename_num++] = xstrdup(g_local_node_name); + g_expect_gucInfo->gucinfo_array[g_expect_gucInfo->gucinfo_num++] = xstrdup(gucconf_file); + (void)write_stderr("expected instance path: [%s]\n", gucconf_file); + } +} + +/* + ****************************************************************************** + 函数:check_env_value + 描述:检查环境变量的值是否合法。 + 输入:input_env_value + 输出:无 + 返回值:void + ****************************************************************************** + */ + // 检查环境变量的值是否合法 +void check_env_value(const char* input_env_value) +{ + // 不合法字符列表 + const char* danger_character_list[] = { "|", ";", "&", "$", "<", ">", "`", "\\", "'", "\"", "{", "}", "(", ")", "[", "]", "~", "*", "?", "!", "\n", NULL }; + int i = 0; + + // 遍历不合法字符列表,检查环境变量的值中是否包含不合法字符 + for (i = 0; danger_character_list[i] != NULL; i++) { + if (strstr(input_env_value, danger_character_list[i]) != NULL) { + // 打印错误信息并退出程序 + fprintf(stderr, _("ERROR: Failed to check environment value: invalid token \"%s\".\n"), danger_character_list[i]); + exit(1); + } + } +} + +/* + ****************************************************************************** + 函数:get_env_value + 描述:获取环境变量的值。 + 输入:env_var(环境变量名),output_env_value(输出环境变量值) + 输出:无 + 返回值:bool + ****************************************************************************** + */ + // 获取环境变量的值 +bool get_env_value(const char* env_var, char* output_env_value) +{ + // 省略函数实现,示例代码中未给出该函数 + return false; +} +/* + * SSH返回值的含义: + * 0 :连接成功,命令执行成功 + * 1 :连接成功,命令执行失败 + * 127 :连接成功,命令执行失败 + * 255 :连接失败 + */ +void printExecErrorMesg(const char* fcmd, const char* nodename) +{ + // 根据不同的返回值输出不同的错误信息 + if (g_remote_command_result == 127) { + write_stderr(_("ERROR: 在节点\"%s\"上执行gs_guc命令:%s 失败,错误码是 %u,请确保gs_guc存在。\n"), fcmd, nodename, g_remote_command_result); + } + else if (g_remote_command_result == 255) { + write_stderr(_("ERROR: 在节点\"%s\"上执行gs_guc命令:%s 失败,错误码是 %u,连接节点\"%s\"失败。\n"), fcmd, nodename, g_remote_command_result, nodename); + } + else if (g_remote_command_result != 0) { + write_stderr(_("ERROR: 在节点\"%s\"上执行gs_guc命令:%s 失败,错误码是 %u,请从当前节点路径\"$GAUSSLOG/bin/gs_guc\"获取更多详细信息。\n"), fcmd, nodename, g_remote_command_result); + } +} + +/* + ****************************************************************************** + 函数名 : is_changed_default_value_failed + 功能 : 修改参数"log_directory"和"audit_directory"的默认值。 + 输入参数 : type 实例类型 + datadir 实例数据目录 + param_name_str 参数名 + index 参数名索引 + gausslog 默认值 + 返回值 : true 修改默认值失败 + false 成功设置默认值 + ****************************************************************************** +*/ +bool is_changed_default_value_failed(int type, char* datadir, char* param_name_str, int index, const char* gausslog) +{ + char local_inst_name[MAX_INSTANCENAME_LEN] = { 0 }; + char log_dir[MAX_VALUE_LEN] = { 0 }; + int32 retval; + int nRet = 0; + + /* 通过数据路径获取本地实例名 */ + retval = get_local_instancename_by_dbpath(datadir, local_inst_name); + if (retval == CLUSTER_CONFIG_ERROR) { + (void)write_stderr("ERROR: 通过数据目录\"%s\"获取实例名称失败。\n", datadir); + return true; + } + + if (type == INSTANCE_COORDINATOR || type == INSTANCE_DATANODE) { + if (0 == strncmp(param_name_str, "log_directory", strlen("log_directory"))) + nRet = snprintf_s(log_dir, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "'%s/pg_log/%s'", gausslog, local_inst_name); + else + nRet = snprintf_s(log_dir, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "'%s/pg_audit/%s'", gausslog, local_inst_name); + securec_check_ss_c(nRet, "\0", "\0"); + } + else { + if (0 == strncmp(param_name_str, "log_directory", strlen("log_directory"))) { + nRet = snprintf_s(log_dir, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "'%s/pg_log/gtm'", gausslog); + securec_check_ss_c(nRet, "\0", "\0"); + } + else { + (void)write_stderr("ERROR: 参数\"%s\"不支持gtm实例。\n", param_name_str); + return true; + } + } + + config_value[index] = xstrdup(log_dir); + is_disable_log_directory = true; + + return false; +} + +/* + ****************************************************************************** + 函数名 : check_AZ_value + 功能 : 检查输入的AZ名称是否合法。 + 输入参数 : AZValue AZ名称 + 返回值 : true 输入的AZ名称正确 + false 输入的AZ名称不正确 + ****************************************************************************** +*/ +bool check_AZ_value(const char* AZValue) +{ + // 由于AZ名称可以由用户定义,因此检查标准应由om模块确保。这里是一个简单的检查 + if (strlen(AZValue) > (CM_AZ_NAME - 1)) { + return false; + } + + return true; +} +//zaizhe +```c +/* + ****************************************************************************** + Function : parse_AZ_result + Description : 将AZ字符串解析为节点名称列表。 + Input :AZValue az名称 + return :NULL 输入的az名称不正确 + other 解析后的结果 + + 函数功能:将传入的AZ字符串解析为节点名称列表。 + + 函数变量及功能: + - `char* AZStr`: 输入的AZ字符串。 + - `const char* data_dir`: 数据目录。 + - `int nRet`: 返回值。 + - `char* vptr`: 指向AZ字符串的指针。 + - `char* vouter_ptr`: 用于保存`strtok_r`函数的上下文。 + - `char* p`: 指向AZ字符串中的当前节点名称。 + - `char delims[] = ","`: 分隔符,用于拆分AZ字符串。 + - `char tmp[MAX_VALUE_LEN]`: 临时字符串存储AZ字符串。 + - `int i`: 循环变量。 + - `char azList[3][MAX_INSTANCENAME_LEN]`: 存储解析后的节点名称。 + - `char* * array`: 字符串数组。 + - `char* buffer`: 缓冲区。 + - `int curlen`: 当前长度。 + - `char* azName`: 指向节点名称的指针。 + - `size_t len`: 长度。 + - `int ind = -1`: 索引。 + - `const int az1_index = 0`: 节点1的索引。 + - `const int az2_index = 1`: 节点2的索引。 + - `const int az3_index = 2`: 节点3的索引。 + - `int resultStatus = 0`: 结果状态。 + + 类似应用实例:假设有一个系统,需要将用户输入的多个选项解析为不同的参数,并进行相应的处理。这个函数可以帮助解析用户输入的选项,并将其存储在一个列表中,以供后续使用。例如,用户输入的选项可以是"option1, option2, option3",则可以使用该函数将其解析为一个包含3个选项的列表。 + + 代码解释: + - 初始化临时AZ字符串和存储AZ字符串的数组。 + - 通过分隔符','拆分AZ字符串。 + - 清除节点名称中的空格。 + - 检查节点名称是否有效。 + - 如果azList[az1_index]为空,则将节点名称存储到azList[az1_index]。 + - 如果azList[az2_index]为空且长度不同或内容不同,则将节点名称存储到azList[az2_index]。 + - 如果azList[az3_index]为空且长度与azList[az1_index]和azList[az2_index]都不同或内容都不同,则将节点名称存储到azList[az3_index]。 + - 继续循环解析AZ字符串的下一个节点。 + - 返回解析后的结果。 + ****************************************************************************** +*/ +char* parse_AZ_result(char* AZStr, const char* data_dir) +{ + int nRet = 0; + char* vptr = NULL; + char* vouter_ptr = NULL; + char* p = NULL; + char delims[] = ","; + char tmp[MAX_VALUE_LEN] = { 0 }; // 临时字符串存储AZ字符串 + int i = 0; + char azList[3][MAX_INSTANCENAME_LEN] = { 0 }; // 存储解析后的节点名称 + char tmpAzName[MAX_INSTANCENAME_LEN] = { 0 }; // 临时字符串存储节点名称 + char** array = NULL; + char* buffer = NULL; + int curlen = 0; + char* azName = NULL; + size_t len = 0; + int ind = -1; + const int az1_index = 0; + const int az2_index = 1; + const int az3_index = 2; + int resultStatus = 0; + + // 初始化临时AZ字符串和存储AZ字符串的数组 + nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = snprintf_s(tmp, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", AZStr); + securec_check_ss_c(nRet, "\0", "\0"); + for (i = 0; i < 3; i++) { + nRet = memset_s(azList[i], MAX_INSTANCENAME_LEN, '\0', MAX_INSTANCENAME_LEN); + securec_check_c(nRet, "\0", "\0"); + } + + // 通过','拆分AZ名称 + vptr = strtok_r(tmp, delims, &vouter_ptr); + while (NULL != vptr) { + p = vptr; + + // 清除空格 + while (isspace((unsigned char)*p)) + p++; + + // 如果节点名称已存在,则跳过,否则存储 + size_t azNameLength = strlen(p); + if (check_AZ_value(p)) { + // 如果azList[az1_index]为空,则存储到azList[az1_index] + if (azList[az1_index][0] == '\0') { + nRet = strncpy_s(azList[az1_index], MAX_INSTANCENAME_LEN, p, azNameLength); + securec_check_c(nRet, "\0", "\0"); + } + // 如果azList[az2_index]为空,则存储到azList[az2_index] + else if (azList[az2_index][0] == '\0') { + // 如果长度相等且内容相等,则跳过 + if (azNameLength != strlen(azList[az1_index]) || + strncmp(p, azList[az1_index], strlen(azList[az1_index])) != 0) { + nRet = strncpy_s(azList[az2_index], MAX_INSTANCENAME_LEN, p, azNameLength); + securec_check_c(nRet, "\0", "\0"); + } + } + // 如果azList[az3_index]为空,则存储到azList[az3_index] + else if (azList[az3_index][0] == '\0') { + // 如果长度不相等且内容不相等,则存储 + if ((azNameLength != strlen(azList[az1_index]) || + strncmp(p, azList[az1_index], strlen(azList[az1_index])) != 0) && + (azNameLength != strlen(azList[az2_index]) || + strncmp(p, azList[az2_index], strlen(azList[az2_index])) != 0)) { + nRet = strncpy_s(azList[az3_index], MAX_INSTANCENAME_LEN, p, azNameLength); + securec_check_c(nRet, "\0", "\0"); + ... + + /* + ****************************************************************************** + Function : get_nodename_number_from_nodelist + Description : 从nodename字符串中获取nodename的数量,字符串使用逗号分隔 + Input :AZValue namelist(nodename字符串) + return :int nodename的数量 + ****************************************************************************** + */ + int get_nodename_number_from_nodelist(const char* namelist) + { + char* ptr = NULL; // 指向每个nodename的指针 + char* outer_ptr = NULL; // strtok_r函数的外部指针,用于保存上一次的位置 + char delims[] = ","; // 分隔符为逗号 + size_t len = 0; // 字符串长度 + int count = 0; // nodename的数量 + char* buffer = NULL; // 用于存储带有null终止符的字符串 + int nRet = 0; // 用于保存snprintf_s函数的返回值 + + len = strlen(namelist) + 1; + buffer = (char*)pg_malloc_zero(len * sizeof(char)); // 分配内存空间 + nRet = snprintf_s(buffer, len, len - 1, "%s", namelist); // 将namelist复制到buffer中 + securec_check_ss_c(nRet, buffer, "\0"); + + ptr = strtok_r(buffer, delims, &outer_ptr); // 第一次调用strtok_r,获取第一个nodename + while (NULL != ptr) { + count++; // nodename数量加1 + ptr = strtok_r(NULL, delims, &outer_ptr); // 继续调用strtok_r,获取下一个nodename + } + + GS_FREE(buffer); // 释放内存空间 + return count; + } + + /* + ****************************************************************************** + Function : parse_datanodename_result + Description : 检查数据节点名称 + Input :datanodenamelist 数据节点名称 + return :NULL 输入的数据节点名称不正确 + other 真实的结果 + ****************************************************************************** + */ + char* ParseDatanameResult(const char* datanodeNameList, const char* dataDir) + { + int nRet; + char* vptr = NULL; // 指向每个节点名称的指针 + char* vouterPtr = NULL; // strtok_r函数的外部指针,用于保存上一次的位置 + char* p = NULL; // 指向每个节点名称的指针 + char delims[] = ","; // 分隔符为逗号 + char tmp[MAX_VALUE_LEN] = { 0 }; // 临时存储节点名称的数组 + char* buffer = NULL; // 用于存储带有null终止符的字符串 + size_t len; + + // 初始化临时nodename字符串,用于存储nodename字符串 + nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = snprintf_s(tmp, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", datanodeNameList); // 将datanodeNameList复制到tmp中 + securec_check_ss_c(nRet, "\0", "\0"); + + vptr = strtok_r(tmp, delims, &vouterPtr); // 第一次调用strtok_r,获取第一个nodename + while (vptr != NULL) { + p = vptr; + + // p 类似于:dn_6001, dn_6002 + while (isspace((unsigned char)*p)) { // 跳过字符串前的空格 + p++; + } + + if (CheckDataNameValue(p, dataDir)) { // 检查节点名称是否正确 + vptr = strtok_r(NULL, delims, &vouterPtr); // 继续调用strtok_r,获取下一个nodename + } + else { + // 输入节点名称不正确 + write_stderr("Notice: datanodename value check failed.(datanodename=%s)\n", p); + return NULL; + } + } + + len = strlen(datanodeNameList) + 1; + // 获取字符串信息 + buffer = (char*)pg_malloc_zero(len * sizeof(char)); // 分配内存空间 + nRet = snprintf_s(buffer, len, (len - 1), "%s", datanodeNameList); // 将datanodeNameList复制到buffer中 + securec_check_ss_c(nRet, buffer, "\0"); + return buffer; + } + + /* + ****************************************************************************** + Function : get_AZ_value + Description : 将AZ字符串解析为节点名称列表。 + Input :value 输入的参数值 + + 这段代码是一个名为`get_AZ_value`的函数,用于将AZ字符串解析为节点名称列表。输入参数为`value`和`data_dir`,返回值为`char*`类型。 + + 代码中定义了多个变量,包括`minLen`、`nRet`、`tmp`、`p`、`q`、`s`、`preStr`、`level`、`i`、`j`、`count`、`nodenameList`、`result`、`len`、`az1`、`vouter_ptr`、`delims`、`vptr`、`emptyvalue`和`isNodeName`。 + + 函数首先判断`az1`是否为空,如果为空则输出错误信息并返回NULL。否则,计算`minLen`的值。 + + 接下来,通过`memset_s`函数将`preStr`和`level`数组的值置为'\0'。 + + 然后,将`value`中的空格和单引号去除,并赋值给`tmp`数组。 + + 然后,判断`value`的长度是否超过最大长度,如果超过则输出错误信息并返回NULL。 + + 接着,将`p`指向`tmp`的首地址,并根据`p`的值进行不同的处理。如果`p`为空或者为'*',则将`emptyvalue`和`p`拼接成新的字符串,并返回。如果`p`以"FIRST"开头,则将"FIRST "拷贝到`preStr`中,并将`p`指向剩余的部分。 + + 同样地,如果`p`以"ANY"开头,则将"ANY "拷贝到`preStr`中,并将`p`指向剩余的部分。 + + 最后,如果`p`以"NODE"开头,则将`isNodeName`标志设置为true,并将`p`指向剩余的部分。 + ****************************************************************************** + */ + char* get_AZ_value(const char* value, const char* data_dir) + { + size_t minLen = 0; // 最小长度 + int nRet = 0; + char tmp[MAX_VALUE_LEN] = { 0 }; // 临时存储字符串的数组 + char* p = NULL; // 指针p + char* q = NULL; // 指针q + char* s = NULL; // 指针s + char preStr[16] = { 0 }; // 存储前缀字符串的数组 + char level[4] = { 0 }; // 存储级别字符串的数组 + int i = 0; // 变量i + int j = 0; // 变量j + int count = 0; // 计数器 + char* nodenameList = NULL; // 节点名称列表 + char* result = NULL; // 结果字符串 + size_t len = 0; // 长度 + char* az1 = getAZNamebyPriority(g_az_master); // 获取AZ名称 + char* vouter_ptr = NULL; // 指针vouter_ptr + char delims[] = ","; // 分隔符 + char* vptr = NULL; // 指针vptr + char emptyvalue[] = "''"; // 空值 + bool isNodeName = false; // 是否为节点名称 + + if (az1 != NULL) { + minLen = strlen("ANY X()") + strlen(az1); // 获取最小长度 + } + else { + (void)write_stderr("ERROR: can not find AZ_MASTER Name, current az_master priority=%u.\n", g_az_master); + return NULL; + } + + nRet = memset_s(preStr, sizeof(preStr) / sizeof(char), '\0', sizeof(preStr) / sizeof(char)); // 将preStr置为'\0' + securec_check_c(nRet, "\0", "\0"); + nRet = memset_s(level, sizeof(level) / sizeof(char), '\0', sizeof(level) / sizeof(char)); // 将level置为'\0' + securec_check_c(nRet, "\0", "\0"); + + /* 值包含空格或者单引号,因此跳过它们 */ + nRet = memset_s(tmp, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); // 将tmp置为'\0' + securec_check_c(nRet, "\0", "\0"); + i = 0; + j = 1; + while (j < (int)strlen(value) - 1) { + if (!isspace(value[j])) { + tmp[i] = value[j]; // 提取非空格字符 + i++; + j++; + } + else { + j++; + } + } + + /* 检查值的长度 */ + if (strlen(value) > MAX_VALUE_LEN) { + (void)write_stderr("ERROR: The value of pamameter synchronous_standby_names is incorrect.\n"); + return NULL; + } + + p = tmp; // p指向tmp的首地址 + if (strlen(p) == 0 || *p == '*') { + len = strlen(emptyvalue) + strlen(p) + 1; + result = (char*)pg_malloc_zero(len * sizeof(char)); + nRet = snprintf_s(result, len, len - 1, "'%s'", p); + securec_check_ss_c(nRet, "\0", "\0"); + return result; + } + + // 给preStr赋值 + /* FIRST 分支 */ + if (0 == strncmp(p, "FIRST", strlen("FIRST"))) { + nRet = strncpy_s(preStr, sizeof(preStr) / sizeof(char), "FIRST ", strlen("FIRST ")); + securec_check_c(nRet, "\0", "\0"); + p = p + strlen("FIRST"); + } + /* ANY 分支 */ + if (0 == strncmp(p, "ANY", strlen("ANY"))) { + nRet = strncpy_s(preStr, sizeof(preStr) / sizeof(char), "ANY ", strlen("ANY ")); + securec_check_c(nRet, "\0", "\0"); + p = p + strlen("ANY"); + } + + if (strncmp(p, "NODE", strlen("NODE")) == 0) { + isNodeName = true; + p = p + strlen("NODE"); + } + + // 其他部分省略 + } + + /* + ****************************************************************************** + Function : do_local_para_value_change + Description : 执行本地参数值更改。只支持参数 "log_directory" 和 "audit_directory"。 + 如果要禁用 "log_directory",使用默认值 "$GAUSSLOG/pg_log/instance_name"。 + 如果要禁用 "audit_directory",使用默认值 "$GAUSSLOG/pg_audit/instance_name"。 + ****************************************************************************** + */ + int do_local_para_value_change(int type, char* datadir) + { + bool is_failed = false; // 标志是否有失败情况 + int i = 0; + char gausslog[MAXPGPATH] = { 0 }; + char staticfile[MAXPGPATH] = { 0 }; + char gausshome[MAXPGPATH] = { 0 }; + int nRet = 0; + struct stat statbuf; + + if (type != INSTANCE_COORDINATOR && type != INSTANCE_DATANODE && type != INSTANCE_CMSERVER && + type != INSTANCE_CMAGENT && type != INSTANCE_GTM) { + (void)write_stderr("ERROR: The instance type is incorrect.\n"); // 输出错误信息 + return FAILURE; // 返回失败 + } + + for (i = 0; i < config_param_number; i++) { + if (0 == strncmp(config_param[i], + "synchronous_standby_names", + strlen(config_param[i]) > strlen("synchronous_standby_names") + ? strlen(config_param[i]) + : strlen("synchronous_standby_names"))) { + if (type != INSTANCE_DATANODE) { + (void)write_stderr( + "ERROR: The pamameter synchronous_standby_names only can be used for datanode type.\n"); + return FAILURE; // 返回失败 + } + + if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) { + g_need_changed = false; // 标志不需要更改 + } + else { + check_env_value(gausshome); + nRet = snprintf_s(staticfile, MAXPGPATH, MAXPGPATH - 1, "%s/bin/%s", gausshome, STATIC_CONFIG_FILE); + securec_check_ss_c(nRet, "\0", "\0"); + if (lstat(staticfile, &statbuf) != 0) { + g_need_changed = false; // 标志不需要更改 + } + else { + if (0 != init_gauss_cluster_config()) { // 初始化高斯集群配置信息 + (void)write_stderr( + "ERROR: Failed to get cluster information from static configuration file.\n"); + return FAILURE; // 返回失败 + } + } + } + /* init g_local_instance_path */ + if (NULL == g_local_instance_path) { + g_local_instance_path = xstrdup(datadir); + } + } + if (NULL == config_value[i] || is_disable_log_directory) { + if (0 == strncmp(config_param[i], "log_directory", strlen("log_directory")) || + 0 == strncmp(config_param[i], "audit_directory", strlen("audit_directory"))) { + if (!get_env_value("GAUSSLOG", gausslog, sizeof(gausslog) / sizeof(char))) + return FAILURE; // 返回失败 + + check_env_value(gausslog); + is_failed = is_changed_default_value_failed(type, datadir, config_param[i], i, gausslog); // 检查是否更改默认值失败 + } + } + } + + if (is_failed) + return FAILURE; // 返回失败 + return SUCCESS; // 返回成功 + } + + int do_local_guc_command(int type, char* temp_datadir) + { + if ('\0' != temp_datadir[0]) { + /* + * When do check, do_local_para_value_change is not be used. + */ + if ((type != INSTANCE_CMAGENT) && (type != INSTANCE_CMSERVER)) { + if ((CHECK_CONF_COMMAND != ctl_command) && (FAILURE == do_local_para_value_change(type, temp_datadir))) + return FAILURE; // 返回失败 + } + + if (0 != process_guc_command(temp_datadir)) + return FAILURE; // 返回失败 + } + return SUCCESS; // 返回成功 + } + /* + ****************************************************************************** + Function : do_command_in_local_node + Description : 在本地节点设置/重新加载guc参数 + Input : type (实例类型) + indatadir (实例数据路径) + Output : None + Return : void + ****************************************************************************** + */ + void do_command_in_local_node(int type, char* indatadir) + { + char datadir[MAXPGPATH] = { 0 }; + + /* 仅在数据目录中处理 */ + if (NULL == indatadir) { + char* envvar = NULL; + // datadir = /* 从PGDATA获取 */ + if ((INSTANCE_COORDINATOR == type) || (INSTANCE_DATANODE == type)) + envvar = "PGDATA"; + else if (INSTANCE_GTM == type) + envvar = "GTMDATA"; + else + return; + + if (!get_env_value(envvar, datadir, sizeof(datadir) / sizeof(char))) + return; + if (NULL != datadir) { + check_env_value(datadir); + } + /* 处理PGDATA / GTMDATA */ + if (checkPath(datadir) != 0) { + write_stderr(_("realpath(%s) failed : %s!\n"), datadir, strerror(errno)); + } + save_expect_instance_info(datadir); + if (FAILURE == do_local_guc_command(type, datadir)) + return; + } + else { + /* 处理-D选项 */ + if (checkPath(indatadir) != 0) { + write_stderr(_("realpath(%s) failed : %s!\n"), indatadir, strerror(errno)); + } + save_expect_instance_info(indatadir); + if (FAILURE == do_local_guc_command(type, indatadir)) + return; + } + } + + /* + ****************************************************************************** + Function : do_command_with_all_option + Description : 使用"-I all"选项设置/重新加载guc参数 + Input : type (实例类型) + indatadir (实例数据路径) + Output : None + Return : void + ****************************************************************************** + */ + void do_command_with_all_option(int type, char* indatadir) + { + if (node_type_number == LARGE_INSTANCE_NUM) + do_command_for_cndn(type, indatadir); + else if (type == INSTANCE_COORDINATOR) + do_command_for_cn_gtm(type, indatadir, true); + else if (type == INSTANCE_GTM) + do_command_for_cn_gtm(type, indatadir, false); + else if (type == INSTANCE_DATANODE) + do_command_for_dn(type, indatadir); + else if ((type == INSTANCE_CMAGENT) || (type == INSTANCE_CMSERVER)) + do_command_for_cm(type, indatadir); + else + return; + } +```cpp +/* + ****************************************************************************** + 函数 : do_command_for_cn + 描述 : 执行命令(用于协调器或者gtm节点) + 输入 : type (实例类型) + indatadir (实例数据路径) + isCoordinator (是否为协调器,true为协调器,false为gtm) + 输出 : 无 + 返回 : void + ****************************************************************************** +*/ +void do_command_for_cn_gtm(int type, char* indatadir, bool isCoordinator) +{ + char temp_datadir[MAXPGPATH] = { 0 }; + errno_t rc = 0; + + if (isCoordinator) { + // 将当前节点的数据路径复制给temp_datadir + rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->DataPath, sizeof(temp_datadir) / sizeof(char)); + } + else { + // 将当前节点的gtmLocalDataPath复制给temp_datadir + rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->gtmLocalDataPath, sizeof(temp_datadir) / sizeof(char)); + } + securec_check_c(rc, "\0", "\0"); + + // 保存预期实例信息 + save_expect_instance_info(temp_datadir); + + // 执行本地的guc命令 + if (FAILURE == do_local_guc_command(type, temp_datadir)) + return; +} + +/* + ****************************************************************************** + 函数 : do_command_for_dn + 描述 : 执行命令(用于datanode节点) + 输入 : type (实例类型) + indatadir (实例数据路径) + 输出 : 无 + 返回 : void + ****************************************************************************** +*/ +void do_command_for_dn(int type, char* indatadir) +{ + char temp_datadir[MAXPGPATH] = { 0 }; + uint32 i = 0; + errno_t rc = 0; + + // 对于每一个本地的datanode节点 + for (i = 0; i < get_local_num_datanode(); i++) { + // 将当前节点的datanodeLocalDataPath复制给temp_datadir + rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->datanode[i].datanodeLocalDataPath, sizeof(temp_datadir) / sizeof(char)); + securec_check_c(rc, "\0", "\0"); + + // 保存预期实例信息 + save_expect_instance_info(temp_datadir); + } + + // 对于每一个本地的datanode节点 + for (i = 0; i < get_local_num_datanode(); i++) { + // 将当前节点的datanodeLocalDataPath复制给temp_datadir + rc = memcpy_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), g_currentNode->datanode[i].datanodeLocalDataPath, sizeof(temp_datadir) / sizeof(char)); + securec_check_c(rc, "\0", "\0"); + + // 执行本地的guc命令 + if (FAILURE == do_local_guc_command(type, temp_datadir)) { + return; + } + } +} + +/* + ****************************************************************************** + 函数 : do_command_for_cm + 描述 : 执行命令(用于cmserver或者cmagent节点) + 输入 : type (实例类型) + indatadir (实例数据路径) + isCmserver (是否为cmserver节点,true为cmserver,false为cmagent) + 输出 : 无 + 返回 : void + ****************************************************************************** +*/ +void do_command_for_cm(int type, char* indatadir) +{ + char temp_datadir[MAXPGPATH] = { 0 }; + char cm_dir[MAXPGPATH] = { 0 }; + int nRet = 0; + errno_t rc = 0; + + // 将当前节点的cmDataPath复制给cm_dir + rc = memcpy_s(cm_dir, sizeof(cm_dir) / sizeof(char), g_currentNode->cmDataPath, sizeof(cm_dir) / sizeof(char)); + securec_check_c(rc, "\0", "\0"); + + if (cm_dir[0] == '\0') { + write_stderr("Failed to get cm base datapath from static config file."); + return; + } + + if (type == INSTANCE_CMAGENT) { + // 将cm_dir和"cm_agent"拼接得到temp_datadir + nRet = snprintf_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), + sizeof(temp_datadir) / sizeof(char) - 1, "%s/cm_agent", cm_dir); + securec_check_ss_c(nRet, "\0", "\0"); + } + else { + if (g_currentNode->cmServerLevel == 1) { + // 将cm_dir和"cm_server"拼接得到temp_datadir + nRet = snprintf_s(temp_datadir, sizeof(temp_datadir) / sizeof(char), + sizeof(temp_datadir) / sizeof(char) - 1, "%s/cm_server", cm_dir); + securec_check_ss_c(nRet, "\0", "\0"); + } + else { + // 节点上没有cmserver实例 + return; + } + } + + // 保存预期实例信息 + save_expect_instance_info(temp_datadir); + + // 执行本地的guc命令 + if (FAILURE == do_local_guc_command(type, temp_datadir)) + return; +} +/* + ****************************************************************************** + Function : do_command_for_datainstance + Description : 执行数据实例的命令 + Input : type (实例类型) + indatadir (实例数据路径) + Output : None + Return : void + ****************************************************************************** +*/ +void do_command_for_cndn(int type, char* indatadir) +{ + // 调用do_command_for_cn_gtm函数,参数为协调器实例类型、实例数据路径和true + do_command_for_cn_gtm(INSTANCE_COORDINATOR, indatadir, true); + // 调用do_command_for_dn函数,参数为数据节点实例类型和实例数据路径 + do_command_for_dn(INSTANCE_DATANODE, indatadir); +} + +/* + ****************************************************************************** + Function : do_command_with_instance_name_option + Description : 使用“-I instance_name”选项设置/重新加载guc参数 + Input : type (实例类型) + instance_name (实例名称) + Output : None + Return : void + ****************************************************************************** +*/ +void do_command_with_instance_name_option(int type, char* instance_name) +{ + // 如果节点类型编号为LARGE_INSTANCE_NUM + if (node_type_number == LARGE_INSTANCE_NUM) { + // 调用do_command_with_instance_name_option_local函数,参数为协调器实例类型和实例名称 + do_command_with_instance_name_option_local(INSTANCE_COORDINATOR, instance_name); + + // 调用do_command_with_instance_name_option_local函数,参数为数据节点实例类型和实例名称 + do_command_with_instance_name_option_local(INSTANCE_DATANODE, instance_name); + } + else { + // 调用do_command_with_instance_name_option_local函数,参数为实例类型和实例名称 + do_command_with_instance_name_option_local(type, instance_name); + } +} + +// 获取cm实例的实际路径 +char* get_cm_real_path(int type) +{ + char* cmpath = NULL; + // 如果实例类型是cmserver + if (INSTANCE_CMSERVER == type) { + // 如果本地节点的cmServerLevel为1且cmDataPath不为空 + if (1 == g_node[g_local_node_idx].cmServerLevel && g_node[g_local_node_idx].cmDataPath[0] != '\0') { + cmpath = xstrdup(g_node[g_local_node_idx].cmDataPath); + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: Failed to get cmserver instance path.\n"); + exit(1); + } + } + // 如果实例类型是cmagent + else if (INSTANCE_CMAGENT == type) { + // 如果本地节点的cmDataPath不为空 + if (g_node[g_local_node_idx].cmDataPath[0] != '\0') { + cmpath = xstrdup(g_node[g_local_node_idx].cmDataPath); + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: Failed to get cmagent instance path.\n"); + exit(1); + } + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: the instance type is incorrect.\n"); + exit(1); + } + return cmpath; +} + +void do_command_with_instance_name_option_local(int type, char* instance_name) +{ + char temp_datadir[MAXPGPATH]; + int rc = 0; + + // 将temp_datadir数组中的元素全部置为'\0' + rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); + securec_check_c(rc, "\0", "\0"); + + // 如果根据实例名称获取本地数据库路径成功 + if (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_SUCCESS) { + // 保存预期的实例信息 + save_expect_instance_info(temp_datadir); + // 如果执行本地的guc命令失败,则返回 + if (FAILURE == do_local_guc_command(type, temp_datadir)) + return; + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); + exit(1); + } +} +/* + ****************************************************************************** + Function : do_command_for_datainstance + Description : 执行数据实例的命令 + Input : type (实例类型) + indatadir (实例数据路径) + Output : None + Return : void + ****************************************************************************** +*/ +void do_command_for_cndn(int type, char* indatadir) +{ + // 调用do_command_for_cn_gtm函数,参数为协调器实例类型、实例数据路径和true + do_command_for_cn_gtm(INSTANCE_COORDINATOR, indatadir, true); + // 调用do_command_for_dn函数,参数为数据节点实例类型和实例数据路径 + do_command_for_dn(INSTANCE_DATANODE, indatadir); +} + +/* + ****************************************************************************** + Function : do_command_with_instance_name_option + Description : 使用“-I instance_name”选项设置/重新加载guc参数 + Input : type (实例类型) + instance_name (实例名称) + Output : None + Return : void + ****************************************************************************** +*/ +void do_command_with_instance_name_option(int type, char* instance_name) +{ + // 如果节点类型编号为LARGE_INSTANCE_NUM + if (node_type_number == LARGE_INSTANCE_NUM) { + // 调用do_command_with_instance_name_option_local函数,参数为协调器实例类型和实例名称 + do_command_with_instance_name_option_local(INSTANCE_COORDINATOR, instance_name); + + // 调用do_command_with_instance_name_option_local函数,参数为数据节点实例类型和实例名称 + do_command_with_instance_name_option_local(INSTANCE_DATANODE, instance_name); + } + else { + // 调用do_command_with_instance_name_option_local函数,参数为实例类型和实例名称 + do_command_with_instance_name_option_local(type, instance_name); + } +} + +// 获取cm实例的实际路径 +char* get_cm_real_path(int type) +{ + char* cmpath = NULL; + // 如果实例类型是cmserver + if (INSTANCE_CMSERVER == type) { + // 如果本地节点的cmServerLevel为1且cmDataPath不为空 + if (1 == g_node[g_local_node_idx].cmServerLevel && g_node[g_local_node_idx].cmDataPath[0] != '\0') { + cmpath = xstrdup(g_node[g_local_node_idx].cmDataPath); + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: Failed to get cmserver instance path.\n"); + exit(1); + } + } + // 如果实例类型是cmagent + else if (INSTANCE_CMAGENT == type) { + // 如果本地节点的cmDataPath不为空 + if (g_node[g_local_node_idx].cmDataPath[0] != '\0') { + cmpath = xstrdup(g_node[g_local_node_idx].cmDataPath); + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: Failed to get cmagent instance path.\n"); + exit(1); + } + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: the instance type is incorrect.\n"); + exit(1); + } + return cmpath; +} + +void do_command_with_instance_name_option_local(int type, char* instance_name) +{ + char temp_datadir[MAXPGPATH]; + int rc = 0; + + // 将temp_datadir数组中的元素全部置为'\0' + rc = memset_s(temp_datadir, MAXPGPATH, '\0', MAXPGPATH); + securec_check_c(rc, "\0", "\0"); + + // 如果根据实例名称获取本地数据库路径成功 + if (get_local_dbpath_by_instancename(instance_name, &type, temp_datadir) == CLUSTER_CONFIG_SUCCESS) { + // 保存预期的实例信息 + save_expect_instance_info(temp_datadir); + // 如果执行本地的guc命令失败,则返回 + if (FAILURE == do_local_guc_command(type, temp_datadir)) + return; + } + else { + // 输出错误信息并退出 + write_stderr("ERROR: Instance name %s is incorrect.\n", instance_name); + exit(1); + } +} + +``` +/* +** 函数名称:do_remote_instance_local +** 功能:在远程节点执行本地实例的操作 +** 参数: +** nodename:节点名称 +** instance_name:实例名称 +** indatadir:实例数据路径 +** 返回值:无 +*/ +void do_remote_instance_local(char* nodename, const char* instance_name, const char* indatadir) +{ + char* command = NULL; + int32 nodeidx; + bool local_mode = !strncmp(g_local_node_name, + nodename, + strlen(g_local_node_name) > strlen(nodename) ? strlen(g_local_node_name) : strlen(nodename)); + command = form_commandline_options(instance_name, indatadir, local_mode); + nodeidx = get_nodeidx_by_name(nodename); + + /* 检查节点名称,确保其在集群静态配置中存在 */ + if (nodeidx < 0) { + write_stderr("ERROR: Node %s not found in static config file\n", nodename); + GS_FREE(command); + exit(1); + } + + (void)execute_guc_command_in_remote_node(nodeidx, command); + + GS_FREE(command); +} + +/* +** 函数名称:do_all_nodes_instance +** 功能:为所有集群节点设置/重载GUC参数 +** 参数: +** instance_name:实例名称 +** indatadir:实例数据路径 +** 返回值:无 +*/ +void do_all_nodes_instance(const char* instance_name, const char* indatadir) +{ + if (node_type_number == LARGE_INSTANCE_NUM) { + nodetype = INSTANCE_COORDINATOR; + do_all_nodes_instance_local(instance_name, indatadir); + + nodetype = INSTANCE_DATANODE; + do_all_nodes_instance_local(instance_name, indatadir); + } + else { + do_all_nodes_instance_local(instance_name, indatadir); + } +} + +/* +** 函数名称:do_all_nodes_instance_local +** 功能:do_all_nodes_instance_local函数。在串行执行检查时,以并行方式执行设置/重载操作 +** 参数: +** instance_name:实例名称 +** indatadir:实例数据路径 +** 返回值:无 +*/ +void do_all_nodes_instance_local(const char* instance_name, const char* indatadir) +{ + if (CHECK_CONF_COMMAND == ctl_command) { + do_all_nodes_instance_local_in_serial(instance_name, indatadir); + } + else { + do_all_nodes_instance_local_in_parallel_loop(instance_name, indatadir); + } +} + +/* +** 函数名称:do_all_nodes_instance_local_in_serial +** 功能:在串行执行检查时,为所有集群节点设置/重载GUC参数 +** 参数: +** instance_name:实例名称 +** indatadir:实例数据路径 +** 返回值:无 +*/ +void do_all_nodes_instance_local_in_serial(const char* instance_name, const char* indatadir) +{ + uint32 idx = 0; + for (idx = 0; idx < get_num_nodes(); idx++) { + char* nodename = getnodename(idx); + bool local_mode = !strncmp(g_local_node_name, + nodename, + strlen(g_local_node_name) > strlen(nodename) ? strlen(g_local_node_name) : strlen(nodename)); + char* command = form_commandline_options(instance_name, indatadir, local_mode); + (void)execute_guc_command_in_remote_node(idx, command); + GS_FREE(command); + } +} + +static void init_global_command() +{ + int i; + int rc = 0; + PARALLEL_COMMAND_S* curr_cxt = NULL; + + g_max_commands_parallel = get_num_nodes(); + g_parallel_command_cxt = (PARALLEL_COMMAND_S*)pg_malloc(g_max_commands_parallel * sizeof(PARALLEL_COMMAND_S)); + + rc = memset_s(g_parallel_command_cxt, + g_max_commands_parallel * sizeof(PARALLEL_COMMAND_S), + '\0', + g_max_commands_parallel * sizeof(PARALLEL_COMMAND_S)); + securec_check_c(rc, "\0", "\0"); + + g_cur_commands_parallel = 0; + for (i = 0; i < g_max_commands_parallel; i++) { + curr_cxt = &g_parallel_command_cxt[i]; + curr_cxt->cur_buf_loc = 0; + rc = memset_s(curr_cxt->readbuf, sizeof(curr_cxt->readbuf), '\0', sizeof(curr_cxt->readbuf)); + securec_check_c(rc, "\0", "\0"); + + curr_cxt->pfp = NULL; + curr_cxt->nodename = xstrdup(getnodename((uint32)i)); + } +} + +static void reset_global_command() +{ + int i; + for (i = 0; i < (int)g_incorrect_nodeInfo->num; i++) { + GS_FREE(g_incorrect_nodeInfo->nodename_array[i]); + } + g_incorrect_nodeInfo->num = 0; + g_cur_commands_parallel = 0; +} +static void do_all_nodes_instance_local_in_parallel_loop(const char* instance_name, const char* indatadir) +{ + int i; + init_global_command(); + write_stderr("Begin to perform the total nodes: %d.\n", g_max_commands_parallel); + for (i = 0; i < LOOP_COUNT; i++) { + do_all_nodes_instance_local_in_parallel(instance_name, indatadir); + if (g_incorrect_nodeInfo->num == 0 || i == LOOP_COUNT - 1) { + break; + } + + write_stderr("Retry to perform the failed nodes: %d.\n", (int32)g_incorrect_nodeInfo->num); + reset_global_command(); + (void)SleepInMilliSec(100); + } + + for (i = 0; i < g_max_commands_parallel; i++) { + GS_FREE(g_parallel_command_cxt[i].nodename); + } + GS_FREE(g_parallel_command_cxt); + + if (g_incorrect_nodeInfo->num == 0) { + (void)write_stderr("ALL: Success to perform gs_guc!\n\n"); + } + else { + (void)write_stderr("ALL: Failure to perform gs_guc!\n\n"); + exit(1); + } +} + +static bool needPassNode(const char* nodename) +{ + int cmpLen = 0; + char* ignoreNode = NULL; + + for (uint32 i = 0; i < g_ignore_nodeInfo->num; i++) { + ignoreNode = g_ignore_nodeInfo->nodename_array[i]; + cmpLen = (strlen(ignoreNode) > strlen(nodename)) ? strlen(ignoreNode) : strlen(nodename); + if (strncmp(ignoreNode, nodename, cmpLen) == 0) { + return true; + } + } + return false; +} + +void do_all_nodes_instance_local_in_parallel(const char* instance_name, const char* indatadir) +{ + int idx = 0; + char* nodename = NULL; + int buf_len = 0; + bool if_for_all_instance = true; + int open_count = 0; + bool is_local_node = false; + char* command_local = form_commandline_options(instance_name, indatadir, true); + char* command_remote = form_commandline_options(instance_name, indatadir, false); + + if ((instance_name != NULL) && (strncmp(instance_name, "all", sizeof("all")) != 0)) { + if_for_all_instance = false; + } + + for (idx = 0; idx < g_max_commands_parallel; idx++) { + if (if_for_all_instance == false && validate_instance_name_for_DN(idx, instance_name) == false) { + continue; + } + /* + * When instance type is INSTANCE_CMSERVER, only the nodes that contain the cm_server instance are setting. + * When instance type is INSTANCE_GTM/INSTANCE_COORDINATOR/INSTANCE_DATANODE, only the nodes that contain the gtm instance are setting. + */ + if ((nodetype == INSTANCE_CMSERVER && 1 != g_node[idx].cmServerLevel) || + (nodetype == INSTANCE_GTM && 1 != g_node[idx].gtm) || + (nodetype == INSTANCE_COORDINATOR && 1 != g_node[idx].coordinate) || + (nodetype == INSTANCE_DATANODE && 0 == g_node[idx].datanodeCount)) { + continue; + } + if (NULL == g_parallel_command_cxt[idx].nodename) { + continue; + } + + nodename = g_parallel_command_cxt[idx].nodename; + if ((g_ignore_nodeInfo != NULL) && needPassNode(nodename)) { + continue; + } + open_count++; + + buf_len = (strlen(g_local_node_name) > strlen(nodename)) ? strlen(g_local_node_name) : strlen(nodename); + is_local_node = (0 == strncmp(g_local_node_name, nodename, buf_len)) ? true : false; + is_local_node ? executePopenCommandsParallel(command_local, idx, is_local_node) : + executePopenCommandsParallel(command_remote, idx, is_local_node); + } + write_stderr("Popen count is %d, Popen success count is %d, Popen failure count is %d.\n", + open_count, g_cur_commands_parallel, (int)g_incorrect_nodeInfo->num); + + readPopenOutputParallel(command_local, if_for_all_instance); + + GS_FREE(command_local); + GS_FREE(command_remote); +} + +/* + * Execute commands in parallel + */ +static void executePopenCommandsParallel(const char* cmd, int idx, bool is_local_node) +{ + int rc = 0; + PARALLEL_COMMAND_S* curr_cxt = NULL; + char* fcmd = NULL; + char* mpprvFile = NULL; + int nRet = 0; + size_t len_fcmd = 0; + char* nodename = NULL; + /* the temp directory that storage gs_guc result information */ + char gausshome[MAXPGPATH] = {0}; + + curr_cxt = &g_parallel_command_cxt[idx]; + nodename = g_parallel_command_cxt[idx].nodename; + + curr_cxt->cur_buf_loc = 0; + rc = memset_s(curr_cxt->readbuf, sizeof(curr_cxt->readbuf), '\0', sizeof(curr_cxt->readbuf)); + securec_check_c(rc, "\0", "\0"); + len_fcmd = strlen(cmd) + strlen(nodename) + NAMEDATALEN + MAXPGPATH; + + if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) { + return; + } + check_env_value(gausshome); + + mpprvFile = GetEnvStr("MPPDB_ENV_SEPARATE_PATH"); + /* execute gs_guc commands by 'ssh' */ + if (mpprvFile == NULL) { + fcmd = (char*)pg_malloc_zero(len_fcmd); + nRet = is_local_node ? snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "%s 2>&1", cmd) : + snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "pssh -s -H %s \"%s\" 2>&1", nodename, cmd); + } else { + if (MAXPGPATH <= strlen(mpprvFile)) { + write_stderr("ERROR: The value of environment variable \"MPPDB_ENV_SEPARATE_PATH\" is too long."); + GS_FREE(mpprvFile); + return; + } + check_env_value(mpprvFile); + len_fcmd = len_fcmd + (int)strlen(mpprvFile); + fcmd = (char*)pg_malloc_zero(len_fcmd); + nRet = is_local_node ? snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "source %s; %s 2>&1", mpprvFile, cmd) : + snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "pssh -s -H %s \"source %s; %s\" 2>&1", nodename, mpprvFile, cmd); + } + securec_check_ss_c(nRet, fcmd, "\0"); + + curr_cxt->pfp = popen(fcmd, "r"); + GS_FREE(fcmd); + GS_FREE(mpprvFile); + + if (NULL != curr_cxt->pfp) { + g_cur_commands_parallel++; + uint32 flags; + int fd = fileno(curr_cxt->pfp); + flags = fcntl(fd, F_GETFL, 0); + flags |= O_NONBLOCK; + (void)fcntl(fd, F_SETFL, flags); + } + else { + g_incorrect_nodeInfo->nodename_array[g_incorrect_nodeInfo->num++] = xstrdup(curr_cxt->nodename); + } + return; +} + +/* + * read popen output parallel + */ +static void readPopenOutputParallel(const char* cmd, bool if_for_all_instance) +{ + int rc = 0; + int idx = 0; + bool read_pending = true; + char* result = NULL; + PARALLEL_COMMAND_S* curr_cxt = g_parallel_command_cxt; + int i = 0; + char* endsp = NULL; + int successNumber = 0; + int failedNumber = 0; + int instance_nums = 0; + uint32 ret = 0; + + if (nodetype == INSTANCE_COORDINATOR) { + instance_nums = get_all_coordinator_num(); + write_stderr("Begin to perform gs_guc for coordinators.\n"); + } else if (nodetype == INSTANCE_DATANODE) { + instance_nums = get_all_datanode_num(); + write_stderr("Begin to perform gs_guc for datanodes.\n"); + } else if (nodetype == INSTANCE_CMSERVER) { + instance_nums = get_all_cmserver_num(); + write_stderr("Begin to perform gs_guc for cm_servers.\n"); + } else if (nodetype == INSTANCE_CMAGENT) { + instance_nums = get_all_cmagent_num(); + write_stderr("Begin to perform gs_guc for cm_agents.\n"); + } else { + instance_nums = get_all_gtm_num(); + write_stderr("Begin to perform gs_guc for gtms.\n"); + } + + result = (char*)pg_malloc_zero(MAX_P_READ_BUF + 1); + while (true == read_pending) { + read_pending = false; + for (idx = 0; idx < g_max_commands_parallel; idx++) { + curr_cxt = g_parallel_command_cxt + idx; + /* pipe closed, stop to read pipe */ + if (NULL == curr_cxt->pfp) { + continue; + } + if (NULL == curr_cxt->nodename) { + continue; + } + + errno = 0; + /* successful get some results from pipe, read again */ + if (fgets(result, MAX_P_READ_BUF - 1, curr_cxt->pfp) != NULL) { + int len = strlen(result); + int hasnewline = false; + + read_pending = true; + if (len > 1 && result[len - 1] == '\n') { + hasnewline = true; + } else if ((curr_cxt->cur_buf_loc + len + 1) < (int)sizeof(curr_cxt->readbuf)) { + rc = strncpy_s(curr_cxt->readbuf + curr_cxt->cur_buf_loc, + sizeof(curr_cxt->readbuf) - curr_cxt->cur_buf_loc, + result, + len + 1); + securec_check_c(rc, "\0", "\0"); + curr_cxt->cur_buf_loc += len; + continue; + } + curr_cxt->readbuf[0] = '\0'; + curr_cxt->cur_buf_loc = 0; + endsp = strstr(result, "WARNING"); + if (NULL != endsp) { + (void)write_stderr("%s", result); + } + endsp = strstr(result, "Success to perform gs_guc"); + if (NULL != endsp) { + successNumber++; + if (NULL != curr_cxt->pfp) { + curr_cxt->retvalue = pclose(curr_cxt->pfp); + curr_cxt->pfp = NULL; + GS_FREE(curr_cxt->nodename); + curr_cxt->nodename = NULL; + } + } + endsp = strstr(result, "Failure to perform gs_guc"); + if (NULL != endsp) { + failedNumber++; + g_incorrect_nodeInfo->nodename_array[g_incorrect_nodeInfo->num++] = xstrdup(curr_cxt->nodename); + if (NULL != curr_cxt->pfp) { + curr_cxt->retvalue = pclose(curr_cxt->pfp); + curr_cxt->pfp = NULL; + ret = (uint32)curr_cxt->retvalue; + g_remote_command_result = WEXITSTATUS(ret); + printExecErrorMesg(cmd, curr_cxt->nodename); + } + } + } + /* no results currently, read again */ + else if (errno == EAGAIN) { + read_pending = true; + (void)SleepInMilliSec(100); + continue; + } + /* failed to get results from pipe, exit */ + else { + curr_cxt->retvalue = pclose(curr_cxt->pfp); + curr_cxt->pfp = NULL; + failedNumber++; + g_incorrect_nodeInfo->nodename_array[g_incorrect_nodeInfo->num++] = xstrdup(curr_cxt->nodename); + if (curr_cxt->retvalue != 0) { + ret = (uint32)curr_cxt->retvalue; + g_remote_command_result = WEXITSTATUS(ret); + printExecErrorMesg(cmd, curr_cxt->nodename); + } + else { + (void)write_stderr("Exception: Failed to get the result from the node %s.\n", curr_cxt->nodename); + } + } + } + + if ((successNumber + failedNumber - g_cur_commands_parallel) >= 0) { + break; + } + if (!read_pending) { + (void)write_stderr("Exception: There are some nodes not executed. %d %d %d\n", + successNumber, failedNumber, g_cur_commands_parallel); + } + (void)SleepInMilliSec(100); + } + (void)write_stderr("Command count is %d, Command success count is %d, Command failure count is %d.\n", + g_cur_commands_parallel, successNumber, failedNumber); + + /*an error happend, close all commands and exit */ + if (0 != failedNumber) { + for (idx = 0; idx < g_max_commands_parallel; idx++) { + curr_cxt = g_parallel_command_cxt + idx; + if (NULL == curr_cxt->pfp) { + continue; + } + /* + * Wait for other nodes to complete, otherwise there will be residual processes. + */ + curr_cxt->retvalue = pclose(curr_cxt->pfp); + } + GS_FREE(result); + + if (if_for_all_instance == false) { + (void)write_stderr("\nTotal nodes: %d. Effective nodes: %d. Failed nodes: %u.\n", + g_cur_commands_parallel, + successNumber + failedNumber, + g_incorrect_nodeInfo->num); + } else { + (void)write_stderr( + "\nTotal nodes: %d. Failed nodes: %u.\n", g_max_commands_parallel, g_incorrect_nodeInfo->num); + } + (void)write_stderr("Failed node names:\n"); + for (i = 0; i < (int32)g_incorrect_nodeInfo->num; i++) { + (void)write_stderr(" [%s]\n", g_incorrect_nodeInfo->nodename_array[i]); + } + } + GS_FREE(result); + /*set DN command '-N all -I instance_name' return total nodes, effective nodes and failied node*/ + if (g_incorrect_nodeInfo->num == 0) { + if (if_for_all_instance == false) { + (void)write_stderr("\nTotal nodes: %d. Effective nodes: %d. Failed nodes: %u.\n", + g_max_commands_parallel, + successNumber + failedNumber, + g_incorrect_nodeInfo->num); + } else { + (void)write_stderr("\nTotal instances: %d. Failed instances: 0.\n", instance_nums); + } + } +} + +static void SleepInMilliSec(uint32_t sleepMs) +{ + struct timespec ts; + ts.tv_sec = (sleepMs - (sleepMs % 1000)) / 1000; + ts.tv_nsec = (sleepMs % 1000) * 1000; + + (void)nanosleep(&ts, NULL); +} +/* + ****************************************************************************** + Function : create_tmp_dir + Description : create a temp directory + Input : pathdir (dirctory name) + Output : None + Return : void + ****************************************************************************** +*/ +void create_tmp_dir(const char* pathdir) +{ + if (NULL == pathdir) { + (void)write_stderr(_("ERROR: failed to create a temp directory: invalid path . \n")); + exit(1); + } + /* check whether directory is exits or not */ + if (-1 == access(pathdir, F_OK)) { + if (mkdir(pathdir, 0700) < 0) { + (void)write_stderr(_("ERROR: could not create directory \"%s\": %s.\n"), pathdir, strerror(errno)); + exit(1); + } + } + + if (-1 == access(pathdir, R_OK | W_OK)) { + (void)write_stderr(_("ERROR: Could not access the specified log path: %s\n"), pathdir); + exit(1); + } +} + +/* + ****************************************************************************** + Function : remove_tmp_dir + Description : remove the temp directory + Input : pathdir (dirctory name) + Output : None + Return : void + ****************************************************************************** +*/ +void remove_tmp_dir(const char* pathdir) +{ + char cmd[MAXPGPATH] = {0}; + int nRet = 0; + if (-1 != access(pathdir, R_OK | W_OK)) { + nRet = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1, "rm -rf %s", pathdir); + securec_check_ss_c(nRet, "", ""); + nRet = gs_system(cmd); + if (nRet != 0) { + (void)write_stderr(_("ERROR: Could not delete directory \"%s\": %s\n"), pathdir, strerror(errno)); + exit(1); + } + } +} +/* + ****************************************************************************** + Function : execute_guc_command_in_remote_node + Description : + Input : idx - node id index + command - gs_guc execute command + Output : None + Return : None + ****************************************************************************** +*/ +int execute_guc_command_in_remote_node(int idx, char* command) +{ + char* nodename = getnodename(idx); + char* fcmd = NULL; + char* mpprvFile = NULL; + size_t len_fcmd = strlen(command) + strlen(nodename) + NAMEDATALEN + MAXPGPATH; + int nRet = 0; + uint32 ret = 0; + /* the temp directory that storage gs_guc result information */ + char gausshome[MAXPGPATH] = {0}; + char sshlogpathdir[MAXPGPATH] = {0}; + char result_file[MAXPGPATH] = {0}; + int pid = 0; + time_t tick; + + if (!get_env_value("GAUSSHOME", gausshome, sizeof(gausshome) / sizeof(char))) + return 1; + check_env_value(gausshome); + /* get the ssh log directory */ + pid = getpid(); + tick = time(NULL); + nRet = snprintf_s(sshlogpathdir, MAXPGPATH, MAXPGPATH - 1, "%s/gs_guc_psshlog_%d_%d", gausshome, (int)tick, pid); + securec_check_ss_c(nRet, "", ""); + + /* create ssh log directory */ + create_tmp_dir((const char*)sshlogpathdir); + /* get the ssh result file name */ + nRet = snprintf_s(result_file, MAXPGPATH, MAXPGPATH - 1, "%s/%s", sshlogpathdir, nodename); + securec_check_ss_c(nRet, "", ""); + + mpprvFile = GetEnvStr("MPPDB_ENV_SEPARATE_PATH"); + /* execute gs_guc commands by 'ssh' */ + if (mpprvFile == NULL) { + fcmd = (char*)pg_malloc_zero(len_fcmd); + if (0 != strncmp(g_local_node_name, + nodename, + strlen(g_local_node_name) > strlen(nodename) ? strlen(g_local_node_name) : strlen(nodename))) { + nRet = snprintf_s( + fcmd, len_fcmd, len_fcmd - 1, "pssh -s -H %s \"%s\" >%s 2>&1", nodename, command, result_file); + } else { + nRet = snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "%s >%s 2>&1", command, result_file); + } + } else { + if (MAXPGPATH <= strlen(mpprvFile)) { + write_stderr("ERROR: The value of environment variable \"MPPDB_ENV_SEPARATE_PATH\" is too long."); + GS_FREE(mpprvFile); + return 1; + } + check_env_value(mpprvFile); + len_fcmd = len_fcmd + (int)strlen(mpprvFile); + fcmd = (char*)pg_malloc_zero(len_fcmd); + if (0 != strncmp(g_local_node_name, + nodename, + strlen(g_local_node_name) > strlen(nodename) ? strlen(g_local_node_name) : strlen(nodename))) { + nRet = snprintf_s(fcmd, + len_fcmd, + len_fcmd - 1, + "pssh -s -H %s \"source %s; %s\" >%s 2>&1", + nodename, + mpprvFile, + command, + result_file); + } else { + nRet = snprintf_s(fcmd, len_fcmd, len_fcmd - 1, "source %s; %s >%s 2>&1", mpprvFile, command, result_file); + } + } + securec_check_ss_c(nRet, fcmd, "\0"); + ret = (uint32)gs_system(fcmd); + g_remote_command_result = WEXITSTATUS(ret); + printExecErrorMesg(fcmd, nodename); + GS_FREE(fcmd); + GS_FREE(mpprvFile); + + if (g_remote_command_result == 255 || g_remote_command_result == 127) { + g_remote_connection_signal = false; + g_incorrect_nodeInfo->nodename_array[g_incorrect_nodeInfo->num++] = xstrdup(nodename); + remove_tmp_dir(sshlogpathdir); + return 1; + } + + if (g_remote_command_result == 1) { + /* save expect instance information into global parameter */ + save_remote_instance_info(result_file, nodename, command, g_expect_gucInfo, false); + } else { + /* save expect and real instance information into global parameter */ + save_remote_instance_info(result_file, nodename, command, g_expect_gucInfo, false); + save_remote_instance_info(result_file, nodename, command, g_real_gucInfo, true); + } + remove_tmp_dir(sshlogpathdir); + return 0; +} + +/* + ****************************************************************************** + Function : is_information_exists + Description : check the instance information whether have been storaged or not + Input : nodename - node name + gucfile - the instance guc config file + Output : None + Return : true the information has been in global parameter + false the information doesn't not in global parameter + ****************************************************************************** +*/ +bool is_information_exists(const char* nodename, const char* gucfile) +{ + uint32 i = 0; + + for (i = 0; i < g_real_gucInfo->nodename_num; i++) { + /* We must makesure that the parameter is exactly equal to array value. So strncmp cann't be used. */ + if (NULL != g_real_gucInfo->nodename_array[i] && NULL != g_real_gucInfo->gucinfo_array[i]) { + if ((0 == strcmp(g_real_gucInfo->nodename_array[i], nodename)) && + (0 == strcmp(g_real_gucInfo->gucinfo_array[i], gucfile))) + return true; + } + } + return false; +} + +/* + ****************************************************************************** + Function : get_keywords + Description : get keywords from the command, the information is used for analysis gs_guc + Input : command + Output : None + Return : keywords + ****************************************************************************** +*/ +char* get_keywords(char* command) +{ + char* keywords = NULL; + + /* get keywords by action type */ + if (!is_hba_conf) { + if (strstr(command, " set ") != NULL) + keywords = xstrdup("gs_guc set:"); + else if (strstr(command, " reload ") != NULL) + keywords = xstrdup("gs_guc reload:"); + else + keywords = xstrdup("gs_guc check:"); + } else { + if (strstr(command, " set ") != NULL) + keywords = xstrdup("gs_guc sethba:"); + else + keywords = xstrdup("gs_guc reloadhba:"); + } + + return keywords; +} + +void save_parameter_info(char* buffer, gucInfo* guc_info) +{ + char* p1 = NULL; + char* p = NULL; + + char* tmp_str = NULL; + char* ptr = NULL; + char* outer_ptr = NULL; + /* + * The result type of check + * expected guc information: NodeName: max_connections=NULL: [$PATH] + * gs_guc check: NodeName: pamameter=value: [$PATH] + */ + /* get the second ':' position */ + p1 = strstr(buffer, ":"); + if (NULL == p1) + return; + p1++; + p = strstr(p1, ":"); + if (NULL == p) + return; + p++; + + /**skip the space and goto the begining of parameter position*/ + while (isspace((unsigned char)*p)) + p++; + tmp_str = xstrdup(p); + + /* + * split with ':', get the result information "parameter=value" + * split with '=', get parameter and value + * both this two, we can makesure the point ptr is not NULL. + */ + ptr = strrchr(tmp_str, ':'); + if (NULL == ptr) { + GS_FREE(tmp_str); + return; + } + *ptr = '\0'; + ptr = strtok_r(tmp_str, "=", &outer_ptr); + if (NULL == ptr) { + GS_FREE(tmp_str); + return; + } + + guc_info->paramname_array[guc_info->paramname_num++] = xstrdup(ptr); + guc_info->paramvalue_array[guc_info->paramvalue_num++] = xstrdup(outer_ptr); + + GS_FREE(tmp_str); +} +/* + ****************************************************************************** + Function : save_remote_instance_info + Description : save the instance information which parse from the result file that + do remote gs_guc set/reload into global parameter + Input : nodename - node name + result_file - the instance guc config file + command - the execute commands + gucInfo - struct of guc information + isRealGucInfo - the struct kind + Output : None + Return : void + ****************************************************************************** +*/ +void save_remote_instance_info( + const char* result_file, const char* nodename, char* command, gucInfo* guc_info, bool isRealGucInfo) +{ + char** all_lines = NULL; + char** line = NULL; + char gucfile[MAXPGPATH] = {0}; + char* keywords = NULL; + char* p = NULL; + char* tmp_str = NULL; // tmp string information + bool is_found = false; + int nRet = 0; + + /* read all informations into buffer */ + all_lines = readfile(result_file, 0); + if (NULL == all_lines) { + write_stderr(_("Failed to read file %s. ERROR: %s\n"), result_file, strerror(errno)); + exit(1); + } + + if (isRealGucInfo) + keywords = get_keywords(command); + else + keywords = xstrdup("expected "); + if (NULL == keywords) { + write_stderr(_("Failed to get key words.\n")); + exit(1); + } + + line = all_lines; + while (*line != NULL) { + if (strstr(*line, keywords) != NULL) { + p = *line; + is_found = false; + if ((int)strlen(p) > MAX_VALUE_LEN) { + (void)write_stderr(_("ERROR: The content of line is too long. Please check and make sure it is " + "correct.\nThe content is \"%s\".\n"), p); + exit(1); + } + + /* + * If we want to parse the result, we must find the position of '[' + * The result type of set/reload + * expected instance path: [$PATH] + * gs_guc set: pamameter=value: [$PATH] + * The result type of check + * expected guc information: NodeName: max_connections=NULL: [$PATH] + * gs_guc check: NodeName: pamameter=value: [$PATH] + */ + while (*p && !(*p == '\n' || *p == '[')) { + if (*p == '\'') { + while (*(++p) && !(*p == '\n' || *p == '\'')); + } + p++; + } + + if (*p == '[') { + is_found = true; + p++; + } + /*skill space*/ + while (isspace((unsigned char)*p)) + p++; + + /* the gucconfig path is startwith '/' */ + if (is_found && *p == '/') { + /* If the gucconfig value in the following format, then skip it*/ + if (0 == strncmp(p, "/postgresql.conf", strlen("/postgresql.conf")) || + 0 == strncmp(p, "/pg_hba.conf", strlen("/pg_hba.conf")) || + 0 == strncmp(p, "/cm_server.conf", strlen("/cm_server.conf")) || + 0 == strncmp(p, "/cm_agent.conf", strlen("/cm_agent.conf")) || + 0 == strncmp(p, "/gtm.conf", strlen("/gtm.conf"))) + continue; + + /*the gs_guc result information is "[gucconfig]\n", so remove ']\n' first.*/ + nRet = strncpy_s(gucfile, sizeof(gucfile) / sizeof(char), p, ((int)strlen(p) - 2)); + securec_check_c(nRet, "\0", "\0"); + if (CHECK_CONF_COMMAND == ctl_command) { + guc_info->nodename_array[guc_info->nodename_num++] = xstrdup(nodename); + guc_info->gucinfo_array[guc_info->gucinfo_num++] = xstrdup(gucfile); + + tmp_str = xstrdup(*line); + (void)save_parameter_info(tmp_str, guc_info); + GS_FREE(tmp_str); + } else { + if (!is_information_exists(nodename, gucfile)) { + guc_info->nodename_array[guc_info->nodename_num++] = xstrdup(nodename); + guc_info->gucinfo_array[guc_info->gucinfo_num++] = xstrdup(gucfile); + } + } + } + } + line++; + } + + GS_FREE(keywords); + freefile(all_lines); +} + +/* + ****************************************************************************** + Function : get_guc_option + Description : write guc option informations into guc_opt + ****************************************************************************** +*/ +char** get_guc_option() +{ + char** guc_line_info = NULL; + + if (nodetype == INSTANCE_COORDINATOR) { + guc_line_info = get_guc_line_info((const char**)cndn_guc_info); + } else if (nodetype == INSTANCE_DATANODE) { + if (NULL != g_lcname) { + guc_line_info = get_guc_line_info((const char**)lc_guc_info); + } else { + guc_line_info = get_guc_line_info((const char**)cndn_guc_info); + } + } else if (nodetype == INSTANCE_CMSERVER) { + guc_line_info = get_guc_line_info((const char**)cmserver_guc_info); + } else if (nodetype == INSTANCE_CMAGENT) { + guc_line_info = get_guc_line_info((const char**)cmagent_guc_info); + } else if (nodetype == INSTANCE_GTM) { + guc_line_info = get_guc_line_info((const char**)gtm_guc_info); + } else { + write_stderr(_("%s: unrecognized -Z parameter.\n"), progname); + exit(1); + } + + return guc_line_info; +} + +/* + ************************************************************************************ + Function: get_guc_line_info + Desc : get guc parameter infomation + ************************************************************************************ +*/ +char** get_guc_line_info(const char** optlines) +{ + int nRet = 0; + int i = 0; + int j = 0; + char* p = NULL; + char* q = NULL; + char tmp_paraname[MAX_PARAM_LEN] = {0}; + char new_paraname[MAX_PARAM_LEN] = {0}; + int paramlen = 0; + char** guc_opt = NULL; + + // allocate memory + guc_opt = (char**)pg_malloc_zero(config_param_number * sizeof(char*)); + for (i = 0; i < config_param_number; i++) { + guc_opt[i] = (char*)pg_malloc_zero(MAX_LINE_LEN * sizeof(char)); + } + + // Check the parameters + if (NULL == optlines) { + (void)write_stderr("ERROR: Faile to read file \"%s\".\n", "cluster_guc.conf"); + + for (i = 0; i < config_param_number; i++) { + GS_FREE(guc_opt[i]); + } + GS_FREE(guc_opt); + + return NULL; + } + + for (i = 0; optlines[i] != NULL; i++) { + p = (char*)optlines[i]; + // remove the spaces in the string + while (isspace((unsigned char)*p)) + p++; + + q = p; + + if (*p == '#' || *p == '[') + continue; + + for (j = 0; j < config_param_number; j++) { + nRet = memset_s(tmp_paraname, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); + securec_check_c(nRet, "\0", "\0"); + make_string_tolower(config_param[j], tmp_paraname, sizeof(tmp_paraname) / sizeof(char)); + nRet = snprintf_s(new_paraname, MAX_PARAM_LEN, MAX_PARAM_LEN - 1, "%s|", tmp_paraname); + securec_check_ss_c(nRet, "\0", "\0"); + + paramlen = strnlen(new_paraname, MAX_PARAM_LEN); + if (0 != strncmp(p, new_paraname, paramlen)) + continue; + + nRet = snprintf_s(guc_opt[j], MAX_LINE_LEN, MAX_LINE_LEN - 1, "%s", q); + securec_check_ss_c(nRet, "\0", "\0"); + } + } + + return guc_opt; +} + +/* + ************************************************************************************ + Function: get_guc_type + Desc : get guc parameter type + Return : UnitType + ************************************************************************************ +*/ +GucParaType get_guc_type(const char* type) +{ + if (0 == strncmp(type, "bool", strlen("bool"))) + return GUC_PARA_BOOL; + else if (0 == strncmp(type, "real", strlen("real"))) + return GUC_PARA_REAL; + else if (0 == strncmp(type, "int", strlen("int"))) + return GUC_PARA_INT; + else if (0 == strncmp(type, "enum", strlen("enum"))) + return GUC_PARA_ENUM; + else if (0 == strncmp(type, "string", strlen("string"))) + return GUC_PARA_STRING; + else + return GUC_PARA_ERROR; +} + +/* + ************************************************************************************ + Function: get_guc_unit + Desc : get guc parameter unit + Return : UnitType + ************************************************************************************ +*/ +UnitType get_guc_unit(const char* unit) +{ + if (0 == strncmp(unit, "kB", strlen("kB"))) + return UNIT_KB; + else if (0 == strncmp(unit, "MB", strlen("MB"))) + return UNIT_MB; + else if (0 == strncmp(unit, "GB", strlen("GB"))) + return UNIT_GB; + else if (0 == strncmp(unit, "ms", strlen("ms"))) + return UNIT_MS; + else if (0 == strncmp(unit, "s", strlen("s"))) + return UNIT_S; + else if (0 == strncmp(unit, "min", strlen("min"))) + return UNIT_MIN; + else if (0 == strncmp(unit, "h", strlen("h"))) + return UNIT_H; + else if (0 == strncmp(unit, "d", strlen("d"))) + return UNIT_D; + else + return UNIT_ERROR; +} + +/* + ************************************************************************************ + Function: do_gucopt_parse + Desc : according to guc option line information, parse them into struct + guc_config_enum_entry + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +int do_gucopt_parse(const char* guc_opt, struct guc_config_enum_entry& guc_variable_list) +{ + char opts[MAX_LINE_LEN]; + int nRet = 0; + char* ptr = NULL; + char* outer_ptr = NULL; + char delims[] = "|"; + GucParaType type_val = GUC_PARA_ERROR; + + nRet = memset_s(opts, MAX_LINE_LEN, '\0', MAX_LINE_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = snprintf_s(opts, MAX_LINE_LEN, MAX_LINE_LEN - 1, "%s", guc_opt); + securec_check_ss_c(nRet, "\0", "\0"); + + /* guc_name */ + ptr = strtok_r(opts, delims, &outer_ptr); + if (NULL != ptr) { + nRet = snprintf_s(guc_variable_list.guc_name, MAX_PARAM_LEN, MAX_PARAM_LEN - 1, "%s", ptr); + securec_check_ss_c(nRet, "\0", "\0"); + } + + /* guc_type */ + ptr = strtok_r(NULL, delims, &outer_ptr); + if (NULL != ptr) { + type_val = get_guc_type(ptr); + if (GUC_PARA_ERROR == type_val) { + (void)write_stderr("ERROR: Failed to parse the guc \"%s\" option. The type \"%s\" is incorrect.\n", + guc_variable_list.guc_name, + ptr); + return FAILURE; + } + guc_variable_list.type = type_val; + } + + /* guc_value */ + ptr = strtok_r(NULL, delims, &outer_ptr); + if (NULL != ptr) { + nRet = snprintf_s(guc_variable_list.guc_value, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", ptr); + securec_check_ss_c(nRet, "\0", "\0"); + } else { + (void)write_stderr( + "ERROR: Failed to parse the guc \"%s\" option. The value range information \"%s\" is incorrect.\n", + guc_variable_list.guc_name, + ptr); + return FAILURE; + } + + /* guc_unit */ + ptr = strtok_r(NULL, delims, &outer_ptr); + if (NULL != ptr) { + if (0 == strncmp(ptr, "NULL", strlen("NULL"))) { + nRet = memset_s(guc_variable_list.guc_unit, MAX_UNIT_LEN, '\0', MAX_UNIT_LEN); + securec_check_c(nRet, "\0", "\0"); + } else { + nRet = snprintf_s(guc_variable_list.guc_unit, MAX_UNIT_LEN, MAX_UNIT_LEN - 1, "%s", ptr); + securec_check_ss_c(nRet, "\0", "\0"); + } + } else { + (void)write_stderr("ERROR: Failed to parse the guc \"%s\" option. The parameter unit is incorrect.\n", + guc_variable_list.guc_name); + return FAILURE; + } + + /* guc_message */ + ptr = strtok_r(NULL, delims, &outer_ptr); + if (NULL != ptr) { + if (0 == strncmp(ptr, "NULL", strlen("NULL"))) { + nRet = memset_s(guc_variable_list.message, MAX_MESG_LEN, '\0', MAX_MESG_LEN); + securec_check_c(nRet, "\0", "\0"); + } else { + nRet = snprintf_s(guc_variable_list.message, MAX_MESG_LEN, MAX_MESG_LEN - 1, "%s", ptr); + securec_check_ss_c(nRet, "\0", "\0"); + } + } else { + (void)write_stderr( + "ERROR: Failed to parse the guc \"%s\" option. The parameter relation message is incorrect.\n", + guc_variable_list.guc_name); + return FAILURE; + } + + ptr = strtok_r(NULL, delims, &outer_ptr); + if (NULL != ptr && '\n' != ptr[0]) { + (void)write_stderr("ERROR: The guc \"%s\" options is incorrect.\n", guc_variable_list.guc_name); + return FAILURE; + } + + return SUCCESS; +} +/* + ************************************************************************************ + Function: check_parameter_name + Desc : according to guc option line information, check the parameter name + guc_opt guc information list + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +int check_parameter_name(char** guc_opt, int type) +{ + int i = 0; + bool is_failed = false; + + if (false == check_parameter_is_valid(type)) + return FAILURE; + else + return SUCCESS; + + for (i = 0; i < config_param_number; i++) { + if (NULL == guc_opt[i] || '\0' == guc_opt[i][0]) { + is_failed = true; + (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect. Please check if the parameters are " + "within the required range.\n", + config_param[i]); + } + } + + if (is_failed) + return FAILURE; + else + return SUCCESS; +} +/* + ************************************************************************************ + Function: check_parameter_is_valid + Desc : according to guc option line information, check the parameter name + guc_opt guc information list + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +bool check_cn_dn_parameter_is_valid() +{ + int para_num = 0; + bool is_valid = false; + bool all_valid = true; + int len = 0; + + char tmp[MAX_PARAM_LEN] = {0}; + int nRet = 0; + + for (para_num = 0; para_num < config_param_number; para_num++) { + nRet = memset_s(tmp, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); + securec_check_c(nRet, "\0", "\0"); + make_string_tolower(config_param[para_num], tmp, sizeof(tmp) / sizeof(char)); + + is_valid = false; + if (NULL != g_lcname) { + for (int i = 0; i < lc_param_number; i++) { + len = strlen(tmp) > strlen(lc_param[i]) ? strlen(tmp) : strlen(lc_param[i]); + if (0 == strncmp(tmp, lc_param[i], len)) { + is_valid = true; + break; + } + } + if (is_valid == false) { + (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect. It is not within the logical " + "cluster support parameters.\n", + config_param[para_num]); + all_valid = false; + } + } else { + for (int i = 0; i < cndn_param_number; i++) { + len = strlen(tmp) > strlen(cndn_param[i]) ? strlen(tmp) : strlen(cndn_param[i]); + if (0 == strncmp(tmp, cndn_param[i], len)) { + is_valid = true; + break; + } + } + if (is_valid == false) { + (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect. It is not within the CN/DN " + "support parameters or it is a read only parameter.\n", + config_param[para_num]); + all_valid = false; + } else if (strncmp(tmp, "enableseparationofduty", strlen("enableseparationofduty")) == 0) { + /* for enableSeparationOfDuty, we give warning */ + (void)write_stderr("WARNING: please take care of the actual privileges of the users " + "while changing enableSeparationOfDuty.\n"); + } +#ifndef USE_ASSERT_CHECKING + /* distribute_test_param only work on debug mode */ + char* distribute_test_param = "distribute_test_param"; + len = strlen(tmp) > strlen(distribute_test_param) ? strlen(tmp) : strlen(distribute_test_param); + if (0 == strncmp(tmp, distribute_test_param, len)) { + all_valid = false; + (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect." + "not work on this mode.\n", + config_param[para_num]); + } + /* segment_test_param only work on debug mode */ + char* segmentTestParam = "segment_test_param"; + len = (strlen(tmp) > strlen(segmentTestParam)) ? strlen(tmp) : strlen(segmentTestParam); + if (strncmp(tmp, segmentTestParam, len) == 0) { + all_valid = false; + (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect." + "not work on this mode.\n", + config_param[para_num]); + } + /* enable_memory_context_check_debug only work on debug mode */ + char* memCtxCheckParam = "enable_memory_context_check_debug"; + len = (strlen(tmp) > strlen(memCtxCheckParam)) ? strlen(tmp) : strlen(memCtxCheckParam); + if (strncmp(tmp, memCtxCheckParam, len) == 0) { + all_valid = false; + (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect." + "not work on this mode.\n", + config_param[para_num]); + } +#endif + } + } + + return all_valid; +} + +bool check_gtm_parameter_is_valid() +{ + int para_num = 0; + bool is_valid = false; + bool all_valid = true; + int len = 0; + + char tmp[MAX_PARAM_LEN] = {0}; + int nRet = 0; + + for (para_num = 0; para_num < config_param_number; para_num++) { + nRet = memset_s(tmp, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); + securec_check_c(nRet, "\0", "\0"); + make_string_tolower(config_param[para_num], tmp, sizeof(tmp) / sizeof(char)); + + is_valid = false; + for (int i = 0; i < gtm_param_number; i++) { + len = strlen(tmp) > strlen(gtm_param[i]) ? strlen(tmp) : strlen(gtm_param[i]); + if (0 == strncmp(tmp, gtm_param[i], len)) { + is_valid = true; + break; + } + } + if (is_valid == false) { + (void)write_stderr( + "ERROR: The name of parameter \"%s\" is incorrect. It is not in the GTM parameter range\n", + config_param[para_num]); + all_valid = false; + } +#ifndef USE_ASSERT_CHECKING + /* distribute_test_param only work on debug mode */ + char* gtm_distribute_test_param = "distribute_test_param"; + len = strlen(tmp) > strlen(gtm_distribute_test_param) ? strlen(tmp) : strlen(gtm_distribute_test_param); + if (0 == strncmp(tmp, gtm_distribute_test_param, len)) { + all_valid = false; + (void)write_stderr("ERROR: The name of parameter \"%s\" is incorrect." + "not work on this mode.\n", + config_param[para_num]); + } +#endif + } + + return all_valid; +} + +bool check_cm_server_parameter_is_valid() +{ + int para_num = 0; + bool is_valid = false; + bool all_valid = true; + int len = 0; + + char tmp[MAX_PARAM_LEN] = {0}; + int nRet = 0; + + for (para_num = 0; para_num < config_param_number; para_num++) { + nRet = memset_s(tmp, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); + securec_check_c(nRet, "\0", "\0"); + make_string_tolower(config_param[para_num], tmp, sizeof(tmp) / sizeof(char)); + + is_valid = false; + for (int i = 0; i < cmserver_param_number; i++) { + len = strlen(tmp) > strlen(cmserver_param[i]) ? strlen(tmp) : strlen(cmserver_param[i]); + if (0 == strncmp(tmp, cmserver_param[i], len)) { + is_valid = true; + break; + } + } + if (is_valid == false) { + (void)write_stderr( + "ERROR: The name of parameter \"%s\" is incorrect. It is not in the CMSERVER parameter range\n", + config_param[para_num]); + all_valid = false; + } + } + + return all_valid; +} + +bool check_cm_agent_parameter_is_valid() +{ + int para_num = 0; + bool is_valid = false; + bool all_valid = true; + int len = 0; + + char tmp[MAX_PARAM_LEN] = {0}; + int nRet = 0; + + for (para_num = 0; para_num < config_param_number; para_num++) { + nRet = memset_s(tmp, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); + securec_check_c(nRet, "\0", "\0"); + make_string_tolower(config_param[para_num], tmp, sizeof(tmp) / sizeof(char)); + + is_valid = false; + for (int i = 0; i < cmagent_param_number; i++) { + len = strlen(tmp) > strlen(cmagent_param[i]) ? strlen(tmp) : strlen(cmagent_param[i]); + if (0 == strncmp(tmp, cmagent_param[i], len)) { + is_valid = true; + break; + } + } + if (is_valid == false) { + (void)write_stderr( + "ERROR: The name of parameter \"%s\" is incorrect. It is not in the CMAGENT parameter range\n", + config_param[para_num]); + all_valid = false; + } + } + + return all_valid; +} + +bool check_parameter_is_valid(int type) +{ + bool is_valid = true; + + if (type == INSTANCE_COORDINATOR || type == INSTANCE_DATANODE) { + is_valid = check_cn_dn_parameter_is_valid(); + } else if (type == INSTANCE_GTM) { + is_valid = check_gtm_parameter_is_valid(); + } else if (type == INSTANCE_CMSERVER) { + is_valid = check_cm_server_parameter_is_valid(); + } else if (type == INSTANCE_CMAGENT) { + is_valid = check_cm_agent_parameter_is_valid(); + } else { + is_valid = false; + (void)write_stderr("ERROR: Node type is not correct.\n"); + } + return is_valid; +} + +/* + ************************************************************************************ + Function: is_parameter_value_error + Desc : check the parameter value. + guc_opt_str guc information string + config_value_str parameter value string + config_param_str parameter name string + Return : false the parameter name is incorrect + true the parameter name is correct + ************************************************************************************ +*/ +bool is_parameter_value_error(const char* guc_opt_str, char* config_value_str, char* config_param_str) +{ + struct guc_config_enum_entry guc_variable_list; + int nRet = 0; + int rc = 0; + int j = 0; + int k = 0; + int len = 0; + bool is_failed = false; + char newvalue[MAX_VALUE_LEN]; + char* ch_position = NULL; + + /* init a struct guc_config_enum_entry that storage guc information */ + guc_variable_list.type = GUC_PARA_ERROR; + nRet = memset_s(guc_variable_list.guc_name, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = memset_s(guc_variable_list.guc_value, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = memset_s(guc_variable_list.guc_unit, MAX_UNIT_LEN, '\0', MAX_UNIT_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = memset_s(guc_variable_list.message, MAX_MESG_LEN, '\0', MAX_MESG_LEN); + securec_check_c(nRet, "\0", "\0"); + + /* parse the guc value string. If FAILURE, return */ + if (FAILURE == do_gucopt_parse(guc_opt_str, guc_variable_list)) { + is_failed = true; + } else { + /* if message is not NULL, print it */ + if ('\0' != guc_variable_list.message[0]) + (void)write_stderr("NOTICE: %s\n", guc_variable_list.message); + + if (0 == strncmp(guc_variable_list.guc_name, "comm_tcp_mode", strlen("comm_tcp_mode"))) + (void)write_stderr( + "WARNING: If the cluster was not restarted, it can not communicate properly after dilatation.\n"); + + nRet = memset_s(newvalue, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); + securec_check_c(nRet, "\0", "\0"); + len = (int)strlen(config_value_str); + + if (guc_variable_list.type == GUC_PARA_ENUM) { + ch_position = strchr(config_value_str,','); + if (ch_position != NULL) { + if (!((config_value_str[0] == '\'' || config_value_str[0] == '"') && + config_value_str[0] == config_value_str[len - 1])) { + (void)write_stderr("ERROR: The value \"%s\" for parameter \"%s\" is incorrect. Please do it like this " + "\"parameter = \'value\'\".\n", + config_value_str, + config_param_str); + exit(1); + } + } + } + + if (guc_variable_list.type == GUC_PARA_INT || guc_variable_list.type == GUC_PARA_REAL || + guc_variable_list.type == GUC_PARA_ENUM || guc_variable_list.type == GUC_PARA_BOOL) { + /* the value like this "XXX" or 'XXXX' */ + if ((config_value_str[0] == '\'' || config_value_str[0] == '"') && + config_value_str[0] == config_value_str[len - 1]) { + for (j = 1, k = 0; j < len - 1 && k < MAX_VALUE_LEN; j++, k++) + newvalue[k] = config_value_str[j]; + } else { + rc = snprintf_s(newvalue, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", config_value_str); + securec_check_ss_c(rc, "\0", "\0"); + } + } else { + if ((config_value_str[0] == '\'' || config_value_str[0] == '"') && + config_value_str[0] == config_value_str[len - 1]) { + rc = snprintf_s(newvalue, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", config_value_str); + securec_check_ss_c(rc, "\0", "\0"); + } else { + (void)write_stderr("ERROR: The value \"%s\" for parameter \"%s\" is incorrect. Please do it like this " + "\"parameter = \'value\'\".\n", + config_value_str, + config_param_str); + exit(1); + } + } + + if (FAILURE == check_parameter_value(config_param_str, + guc_variable_list.type, + guc_variable_list.guc_value, + guc_variable_list.guc_unit, + newvalue)) { + is_failed = true; + + if (guc_variable_list.type >= 0 && + guc_variable_list.type < (GucParaType)(sizeof(value_type_list) / sizeof(value_type_list[0]))) { + (void) write_stderr("ERROR: The value \"%s\" for parameter \"%s\" is incorrect, requires a %s value\n", + config_value_str, config_param_str, value_type_list[guc_variable_list.type]); + } else { + (void) write_stderr("ERROR: The value \"%s\" for parameter \"%s\" is incorrect.\n", config_value_str, + config_param_str); + } + } + } + + return is_failed; +} +/* + ************************************************************************************ + Function: check_parameter + Desc : a interface that do patameter name and value checking. + if value is NULL, it means that we will disable the parameter + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +int check_parameter(int type) +{ + char** guc_opt = NULL; + bool is_failed = false; + int i = 0; + + guc_opt = get_guc_option(); + + /* First we must makesure that the guc information list is correct */ + if (NULL == guc_opt) + return FAILURE; + + if (FAILURE == check_parameter_name(guc_opt, type)) { + /* free guc_opt */ + for (i = 0; i < config_param_number; i++) { + GS_FREE(guc_opt[i]); + } + GS_FREE(guc_opt); + + return FAILURE; + } + + for (i = 0; i < config_param_number; i++) { + /* When config value is not NULL and the value is error, set 'is_failed=true' */ + if (NULL != config_value[i] && is_parameter_value_error(guc_opt[i], config_value[i], config_param[i])) + is_failed = true; + } + + /* free guc_opt */ + for (i = 0; i < config_param_number; i++) { + GS_FREE(guc_opt[i]); + } + GS_FREE(guc_opt); + + if (is_failed) + return FAILURE; + else + return SUCCESS; +} + +/* + ************************************************************************************ + Function: check_parameter_value + Desc : do parameter value checking + Input : paraname paraname + type paratype + guc_list_value + guc_list_unit + value + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +int check_parameter_value( + const char* paraname, GucParaType type, char* guc_list_value, const char* guc_list_unit, const char* value) +{ + if (type == GUC_PARA_INT) + return check_int_real_type_value(paraname, guc_list_value, guc_list_unit, value, true); + else if (type == GUC_PARA_REAL) + return check_int_real_type_value(paraname, guc_list_value, guc_list_unit, value, false); + else if (type == GUC_PARA_ENUM) + return check_enum_type_value(paraname, guc_list_value, value); + else if (type == GUC_PARA_BOOL) + return check_bool_type_value(value); + else if (type == GUC_PARA_STRING) { + return check_string_type_value(paraname, value); + } + else + return FAILURE; +} + +/* + ************************************************************************************ + Function: get_guc_minmax_value + Desc : get min and max value from guc config + Input : guc_list_value value from guc config + guc_minmax_value a struct that storage min and max value string + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +int get_guc_minmax_value(const char* guc_list_val, struct guc_minmax_value& value_list) +{ + char guc_val[MAX_VALUE_LEN]; + int nRet = 0; + char* ptr = NULL; + char* outer_ptr = NULL; + char delims[] = ","; + + nRet = memset_s(guc_val, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = snprintf_s(guc_val, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", guc_list_val); + securec_check_ss_c(nRet, "\0", "\0"); + + /* min value string */ + ptr = strtok_r(guc_val, delims, &outer_ptr); + if (NULL != ptr) { + nRet = snprintf_s(value_list.min_val_str, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", ptr); + securec_check_ss_c(nRet, "\0", "\0"); + } else { + (void)write_stderr("ERROR: The minimum value information is incorrect.\n"); + return FAILURE; + } + + /* max value string */ + ptr = strtok_r(NULL, delims, &outer_ptr); + if (NULL != ptr) { + nRet = snprintf_s(value_list.max_val_str, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", ptr); + securec_check_ss_c(nRet, "\0", "\0"); + } else { + (void)write_stderr("ERROR: The maximum value information is incorrect.\n"); + return FAILURE; + } + + ptr = strtok_r(NULL, delims, &outer_ptr); + if (NULL != ptr) { + (void)write_stderr("ERROR: The minmax information for parameter is incorrect.\n"); + return FAILURE; + } + + return SUCCESS; +} + +/* + ************************************************************************************ + Function: is_alpha_in_string + Desc : judge the string contains alpha or not + Input : str + Return : true the string contains alpha + false the string does not contain alpha + ************************************************************************************ +*/ +bool is_alpha_in_string(const char* str) +{ + const char* p = str; + + while ('\0' != *p) { + if (isalpha(*p)) + return true; + + p++; + } + return false; +} +/* + ************************************************************************************ + Function: is_string_in_list + Desc : judge the string in list or not + Input : str string name + str_list string name list + list_nums the length of str list + Return : true the string is in value_list + false the string is not in value_list + ************************************************************************************ +*/ +bool is_string_in_list(const char* str, const char** str_list, int list_nums) +{ + int i = 0; + char tmp[MAX_PARAM_LEN]; + int nRet = 0; + + nRet = memset_s(tmp, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); + securec_check_c(nRet, "\0", "\0"); + make_string_tolower(str, tmp, sizeof(tmp) / sizeof(char)); + + for (i = 0; i < list_nums; i++) { + if (0 == strcmp(str_list[i], tmp)) + return true; + } + + return false; +} +/* + ************************************************************************************ + Function: check_int_value + Desc : check the int parameter value + Input : paraname parameter name + guc_list_value value from guc config + value parameter value string, including unit + int_newval the parameter value, only including number + int_min_val the min parameter value + int_max_val the max parameter value + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +int check_int_value(const char* paraname, const struct guc_minmax_value& value_list, const char* value, + int64 int_newval, int64 int_min_val, int64 int_max_val) +{ + /* a signal that storage whether the paraname in unit_eight_kB_parameter_list or not */ + bool is_in_list = false; + bool is_exists_alpha = false; + /* makesure the min/max value from guc config list file is correct */ + if ((FAILURE == parse_value(paraname, value_list.min_val_str, NULL, &int_min_val, NULL, true)) || + (FAILURE == parse_value(paraname, value_list.max_val_str, NULL, &int_max_val, NULL, true))) { + (void)write_stderr("ERROR: The minmax value of parameter \"%s\" requires an integer value.\n", paraname); + return FAILURE; + } + /*modify the min/max value , if the parameter unit is 8kB and the value contains unit string. + if the unit is incorrect, when do parse_value by config_value, it will print error messages and exit. + So, we can makesure the unit is correct, if it is exists + */ + is_in_list = is_string_in_list(paraname, unit_eight_kB_parameter_list, lengthof(unit_eight_kB_parameter_list)); + is_exists_alpha = is_alpha_in_string(value); + if (is_in_list && is_exists_alpha) { + int_newval = int_newval / PAGE_SIZE; + } + /* if int_newval < int_min_val or int_newval > int_max_val, print error message */ + if (int_newval < int_min_val || int_newval > int_max_val) { + if (is_in_list && is_exists_alpha) { + (void)write_stderr( + "Notice: The default unit for parameter \"%s\" is disk page and each page is usually %dkB.\n", + paraname, + PAGE_SIZE); + (void)write_stderr("ERROR: The value \"%s\" is outside the valid range for parameter \"%s\" (" INT64_FORMAT + " .. " INT64_FORMAT ").\n", + value, + paraname, + int_min_val, + int_max_val); + } else { + (void)write_stderr("ERROR: The value " INT64_FORMAT + " is outside the valid range for parameter \"%s\" (" INT64_FORMAT " .. " INT64_FORMAT + ").\n", + int_newval, + paraname, + int_min_val, + int_max_val); + } + return FAILURE; + } + return SUCCESS; +} + +/* + ************************************************************************************ + Function: check_real_value + Desc : check the real parameter value + Input : paraname parameter name + guc_list_value value from guc config + double_newval the parameter value + double_min_val the min parameter value + double_max_val the max parameter value + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +int check_real_value(const char* paraname, const struct guc_minmax_value& value_list, double double_newval, + double double_min_val, double double_max_val) +{ + /* makesure the min/max value from guc config list file is correct */ + if ((FAILURE == parse_value(paraname, value_list.min_val_str, NULL, NULL, &double_min_val, false)) || + (FAILURE == parse_value(paraname, value_list.max_val_str, NULL, NULL, &double_max_val, false))) { + (void)write_stderr("ERROR: The minmax value of parameter \"%s\" requires a numeric value.\n", paraname); + return FAILURE; + } + /* if double_newval < double_min_val - DOUBLE_PRECISE or double_newval > double_max_val + DOUBLE_PRECISE, print + * error message */ + if (double_newval < double_min_val - DOUBLE_PRECISE || double_newval > double_max_val + DOUBLE_PRECISE) { + (void)write_stderr("ERROR: The value %g is outside the valid range for parameter \"%s\" (%g .. %g).\n", + double_newval, + paraname, + double_min_val, + double_max_val); + return FAILURE; + } + return SUCCESS; +} + +/* + ************************************************************************************ + Function: check_int_real_type_value + Desc : check the int/real parameter value + Input : paraname parameter name + guc_list_value value from guc config + value parameter value + isInt true is int, false is real + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +int check_int_real_type_value( + const char* paraname, const char* guc_list_value, const char* guc_list_unit, const char* value, bool isInt) +{ + int64 int_newval = INT_MIN; + int64 int_min_val = LLONG_MIN; + int64 int_max_val = LLONG_MAX; + double double_newval = LLONG_MIN; + double double_min_val = LLONG_MIN; + double double_max_val = LLONG_MIN; + struct guc_minmax_value value_list; + int nRet = 0; + + /* init a struct guc_minmax_value that storage guc min/max value information */ + nRet = memset_s(value_list.min_val_str, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = memset_s(value_list.max_val_str, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); + securec_check_c(nRet, "\0", "\0"); + + /* parse int_newval/double_newval value*/ + if (FAILURE == parse_value(paraname, value, guc_list_unit, &int_newval, &double_newval, isInt)) { + if (isInt) + (void)write_stderr("ERROR: The parameter \"%s\" requires an integer value.\n", paraname); + else + (void)write_stderr("ERROR: The parameter \"%s\" requires a numeric value.\n", paraname); + return FAILURE; + } + + /* get min/max value from guc config file */ + if (FAILURE == get_guc_minmax_value(guc_list_value, value_list)) + return FAILURE; + + if ('\0' == value_list.min_val_str[0] || '\0' == value_list.max_val_str[0]) { + (void)write_stderr("ERROR: The minmax information for parameter \"%s\" is incorrect.\n", paraname); + return FAILURE; + } + + if (isInt) + return check_int_value(paraname, value_list, value, int_newval, int_min_val, int_max_val); + else + return check_real_value(paraname, value_list, double_newval, double_min_val, double_max_val); +} + +/* + ************************************************************************************ + Function: parse_value + Desc : parese value from guc config file. + paraname parameter name + value parameter value + guc_list_unit the unit of parameter from guc config file + result_int the parse result about int + result_double the parse result about double + isInt true is int, false is real + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +int parse_value(const char* paraname, const char* value, const char* guc_list_unit, int64* result_int, + double* result_double, bool isInt) +{ + int64 int_val = INT_MIN; + double double_val; + long double tmp_double_val; + char* endptr = NULL; + UnitType unitval = UNIT_ERROR; + bool contain_space = false; + + if (NULL != result_int) + *result_int = 0; + if (NULL != result_double) + *result_double = 0; + + errno = 0; + if (isInt) { + /* transform value into long int */ + int_val = strtoll(value, &endptr, 0); + if (endptr == value || errno == ERANGE) + return FAILURE; + tmp_double_val = (long double)int_val; + } else { + /* transform value into double */ + double_val = strtod(value, &endptr); + if (endptr == value || errno == ERANGE) + return FAILURE; + tmp_double_val = (long double)double_val; + } + + /* skill the blank */ + while (isspace((unsigned char)*endptr)) { + endptr++; + contain_space = true; + } + + if ('\0' != *endptr) { + /* if unit is NULL, it means the value is incorrect */ + if (NULL == guc_list_unit || '\0' == guc_list_unit[0]) + return FAILURE; + + if (contain_space) { + (void)write_stderr("ERROR: There should not hava space between value and unit.\n"); + return FAILURE; + } + + unitval = get_guc_unit(guc_list_unit); + if (UNIT_ERROR == unitval) { + (void)write_stderr("ERROR: Invalid units for this parameter \"%s\".\n", paraname); + return FAILURE; + } else if (UNIT_KB == unitval) { + if (strncmp(endptr, "kB", 2) == 0) { + endptr += 2; + } else if (strncmp(endptr, "MB", 2) == 0) { + endptr += 2; + tmp_double_val *= KB_PER_MB; + } else if (strncmp(endptr, "GB", 2) == 0) { + endptr += 2; + tmp_double_val *= KB_PER_GB; + } else { + (void)write_stderr( + "ERROR: Valid units for this parameter \"%s\" are \"kB\", \"MB\" and \"GB\".\n", paraname); + return FAILURE; + } + } else if (UNIT_MB == unitval) { + if (strncmp(endptr, "MB", 2) == 0) { + endptr += 2; + } else if (strncmp(endptr, "GB", 2) == 0) { + endptr += 2; + tmp_double_val *= MB_PER_GB; + } else { + (void)write_stderr("ERROR: Valid units for this parameter \"%s\" are \"MB\" and \"GB\".\n", paraname); + return FAILURE; + } + } else if (UNIT_GB == unitval) { + if (strncmp(endptr, "GB", 2) == 0) { + endptr += 2; + } else { + (void)write_stderr("ERROR: Valid units for this parameter \"%s\" is \"GB\".\n", paraname); + return FAILURE; + } + } else if (UNIT_MS == unitval) { + if (strncmp(endptr, "ms", 2) == 0) { + endptr += 2; + } else if (strncmp(endptr, "s", 1) == 0) { + endptr += 1; + tmp_double_val *= MS_PER_S; + } else if (strncmp(endptr, "min", 3) == 0) { + endptr += 3; + tmp_double_val *= MS_PER_MIN; + } else if (strncmp(endptr, "h", 1) == 0) { + endptr += 1; + tmp_double_val *= MS_PER_H; + } else if (strncmp(endptr, "d", 1) == 0) { + endptr += 1; + tmp_double_val *= MS_PER_D; + } else { + (void)write_stderr( + "ERROR: Valid units for this parameter \"%s\" are \"ms\", \"s\", \"min\", \"h\", and \"d\".\n", + paraname); + return FAILURE; + } + } else if (UNIT_S == unitval) { + if (strncmp(endptr, "s", 1) == 0) { + endptr += 1; + } else if (strncmp(endptr, "min", 3) == 0) { + endptr += 3; + tmp_double_val *= S_PER_MIN; + } else if (strncmp(endptr, "h", 1) == 0) { + endptr += 1; + tmp_double_val *= S_PER_H; + } else if (strncmp(endptr, "d", 1) == 0) { + endptr += 1; + tmp_double_val *= S_PER_D; + } else { + (void)write_stderr( + "ERROR: Valid units for this parameter \"%s\" are \"s\", \"min\", \"h\", and \"d\".\n", paraname); + return FAILURE; + } + } else if (UNIT_MIN == unitval) { + if (strncmp(endptr, "min", 3) == 0) { + endptr += 3; + } else if (strncmp(endptr, "h", 1) == 0) { + endptr += 1; + tmp_double_val *= MIN_PER_H; + } else if (strncmp(endptr, "d", 1) == 0) { + endptr += 1; + tmp_double_val *= MIN_PER_D; + } else { + (void)write_stderr( + "ERROR: Valid units for this parameter \"%s\" are \"min\", \"h\", and \"d\".\n", paraname); + return FAILURE; + } + } else if (UNIT_H == unitval) { + if (strncmp(endptr, "h", 1) == 0) { + endptr += 1; + } else if (strncmp(endptr, "d", 1) == 0) { + endptr += 1; + tmp_double_val *= H_PER_D; + } else { + (void)write_stderr( + "ERROR: Valid units for this parameter \"%s\" are \"min\", \"h\", and \"d\".\n", paraname); + return FAILURE; + } + } else if (UNIT_D == unitval) { + if (strncmp(endptr, "d", 1) == 0) { + endptr += 1; + } else { + (void)write_stderr("ERROR: Valid units for this parameter \"%s\" is \"d\".\n", paraname); + return FAILURE; + } + } else { + return FAILURE; + } + } + + while (isspace((unsigned char)*endptr)) + endptr++; + + if (*endptr != '\0') + return FAILURE; + + if (isInt) { + if (tmp_double_val > LLONG_MAX || tmp_double_val < LLONG_MIN) + return FAILURE; + if (NULL != result_int) + *result_int = (int64)tmp_double_val; + } else { + if (NULL != result_double) + *result_double = (double)tmp_double_val; + } + + return SUCCESS; +} + +int is_value_in_range(const char* guc_list_value, const char* value) +{ + char* ptr = NULL; + char* outer_ptr = NULL; + char delims[] = ","; + char guc_val[MAX_VALUE_LEN] = {0}; + int nRet = 0; + + nRet = memset_s(guc_val, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = snprintf_s(guc_val, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", guc_list_value); + securec_check_ss_c(nRet, "\0", "\0"); + + ptr = strtok_r(guc_val, delims, &outer_ptr); + while (NULL != ptr) { + if (0 == strcmp(ptr, value)) + return SUCCESS; + else + ptr = strtok_r(NULL, delims, &outer_ptr); + } + return FAILURE; +} + +/************************************************************************************* + Function: check_enum_type_value + Desc : check the parameter value of enum type. + Input : paraname parameter name + guc_list_value the string from config file + value parameter value + Return : SUCCESS + FAILURE + *************************************************************************************/ +int check_enum_type_value(const char* paraname, char* guc_list_value, const char* value) +{ + char guc_val[MAX_VALUE_LEN] = {0}; + int nRet = 0; + const char* vptr = NULL; + char* vouter_ptr = NULL; + const char* p = NULL; + char delims[] = ","; + char tmp_paraname[MAX_PARAM_LEN]; + + nRet = memset_s(guc_val, MAX_VALUE_LEN, '\0', MAX_VALUE_LEN); + securec_check_c(nRet, "\0", "\0"); + nRet = snprintf_s(guc_val, MAX_VALUE_LEN, MAX_VALUE_LEN - 1, "%s", guc_list_value); + securec_check_ss_c(nRet, "\0", "\0"); + nRet = memset_s(tmp_paraname, MAX_PARAM_LEN, '\0', MAX_PARAM_LEN); + securec_check_c(nRet, "\0", "\0"); + + if (NULL == guc_list_value || '\0' == guc_list_value[0]) { + (void)write_stderr("ERROR: Failed to obtain the range information of parameter \"%s\".\n", paraname); + return FAILURE; + } + + make_string_tolower(value, tmp_paraname, sizeof(tmp_paraname) / sizeof(char)); + if (tmp_paraname != NULL && strlen(tmp_paraname) > 0) { + vptr = strtok_r(tmp_paraname, delims, &vouter_ptr); + } else { + vptr = ""; + } + while (NULL != vptr) { + p = vptr; + while (isspace((unsigned char)*p)) + p++; + if (SUCCESS == is_value_in_range(guc_val, p)) { + vptr = strtok_r(NULL, delims, &vouter_ptr); + } else { + (void)write_stderr("ERROR: The value \"%s\" is outside the valid range(%s) for parameter \"%s\".\n", + value, + guc_list_value, + paraname); + return FAILURE; + } + } + return SUCCESS; +} + +/* + ************************************************************************************ + Function: check_bool_type_value + Desc : check the parameter value of bool type. + GUC_PARA_BOOL - in bool value list + Return : SUCCESS + FAILURE + ************************************************************************************ +*/ +int check_bool_type_value(const char* value) +{ + /* the length of value list */ + int list_nums = lengthof(guc_bool_valuelist); + + if (is_string_in_list(value, guc_bool_valuelist, list_nums)) + return SUCCESS; + else + return FAILURE; +} + +static bool check_datestyle_gs_guc(const char* paraname, const char* value) +{ + // datestyleList should be consistent with check_datestyle() in variable.cpp + const char* dateStyleList = "iso,sql,postgres,german"; + const char* dateOrderList = "ymd,dmy,euro,european,mdy,us,noneuro,noneuropean,default"; + + char* rawstring = NULL; + const char delims[] = ","; + char* vptr = NULL; + char* vouter_ptr = NULL; + char* p = NULL; + bool hasDateStyle = false; + bool hasDateOrder = false; + bool hasConflict = false; + char* pname = xstrdup(paraname); + + make_string_tolower(paraname, pname, (int)strlen(pname)); + if (strcmp(pname, "datestyle") != 0) { + // not datestyle, do not change result + free(pname); + return true; + } + + /* Need a modifiable copy of string */ + rawstring = xstrdup(value); + make_string_tolower(value, rawstring, (int)strlen(rawstring)); + // remove last '\'' or space + p = rawstring + strlen(rawstring) - 1; + while (isspace((unsigned char)*p) || *p == '\'') { + *p = '\0'; + p--; + } + + vptr = strtok_r(rawstring, delims, &vouter_ptr); + while (vptr != NULL) { + p = vptr; + while (isspace((unsigned char)*p) || *p == '\'') + p++; + if (is_value_in_range(dateStyleList, p) == SUCCESS) { + if (!hasDateStyle) { + hasDateStyle = true; + } else { + hasConflict = true; + break; + } + vptr = strtok_r(NULL, delims, &vouter_ptr); + } else if (is_value_in_range(dateOrderList, p) == SUCCESS) { + if (!hasDateOrder) { + hasDateOrder = true; + } else { + hasConflict = true; + break; + } + vptr = strtok_r(NULL, delims, &vouter_ptr); + } else { + write_stderr("ERROR: The value \"%s\" is invalid for parameter datestyle.\n", value); + free(rawstring); + free(pname); + return false; + } + } + + free(rawstring); + free(pname); + if (hasConflict) { + write_stderr("ERROR: The value \"%s\" have conflict options for parameter datestyle.\n", value); + } + return hasConflict ? false : true; +} + +/************************************************************************************* + Function: check_string_type_value + Desc : check the paraname value of string type. + GUC_PARA_STRING - length(str) > 0 + Return : SUCCESS + FAILURE + *************************************************************************************/ +int check_string_type_value(const char* paraname, const char* value) +{ + bool result = ((int)strlen(value) > 0) ? true : false; + /* + * For now, we only check value for datestyle. + * If we want to check more value, it is better to use hooks. + */ + if (result && paraname != NULL) { + result = check_datestyle_gs_guc(paraname, value); + } + return result ? SUCCESS : FAILURE; +} + +/* + * GetEnvStr + * + * Note: malloc space for get the return of getenv() function, then return the malloc space. + * so, this space need be free. + */ +static char* GetEnvStr(const char* env) +{ + char* tmpvar = NULL; + const char* temp = getenv(env); + errno_t rc = 0; + if (temp != NULL) { + size_t len = strlen(temp); + if (0 == len) + return NULL; + tmpvar = (char*)malloc(len + 1); + if (tmpvar != NULL) { + rc = strcpy_s(tmpvar, len + 1, temp); + securec_check_c(rc, "\0", "\0"); + return tmpvar; + } + } + return NULL; +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ -- 2.34.1 From 22837de14e9cf715911d271e22955d2a16212cc6 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:12:30 +0800 Subject: [PATCH 15/56] Delete 'src/gausskernel/bootstrap/bootstrap.cpp' --- src/gausskernel/bootstrap/bootstrap.cpp | 1029 ----------------------- 1 file changed, 1029 deletions(-) delete mode 100755 src/gausskernel/bootstrap/bootstrap.cpp diff --git a/src/gausskernel/bootstrap/bootstrap.cpp b/src/gausskernel/bootstrap/bootstrap.cpp deleted file mode 100755 index f9bb0b14d..000000000 --- a/src/gausskernel/bootstrap/bootstrap.cpp +++ /dev/null @@ -1,1029 +0,0 @@ -/* ------------------------------------------------------------------------- - * - * bootstrap.c - * routines to support running openGauss in 'bootstrap' mode - * bootstrap mode is used to create the initial template database - * - * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * Portions Copyright (c) 2010-2012 Postgres-XC Development Group - * - * IDENTIFICATION - * src/backend/bootstrap/bootstrap.c - * - * ------------------------------------------------------------------------- - */ -#include "postgres.h" -#include "knl/knl_variable.h" -#include "pgstat.h" -#include -#include -#include -#ifdef HAVE_GETOPT_H -#include -#endif - -#include "access/tableam.h" -#include "bootstrap/bootstrap.h" -#include "catalog/index.h" -#include "catalog/pg_collation.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_type.h" -#include "libpq/pqsignal.h" -#include "miscadmin.h" -#include "pgstat.h" -#include "nodes/makefuncs.h" -#include "postmaster/aiocompleter.h" -#include "postmaster/bgwriter.h" -#include "postmaster/pagewriter.h" -#include "postmaster/cbmwriter.h" -#include "postmaster/startup.h" -#include "postmaster/twophasecleaner.h" -#include "postmaster/licensechecker.h" -#include "postmaster/walwriter.h" -#include "postmaster/lwlockmonitor.h" -#include "replication/walreceiver.h" -#include "replication/datareceiver.h" -#include "storage/buf/bufmgr.h" -#include "storage/ipc.h" -#include "storage/proc.h" -#include "tcop/tcopprot.h" -#include "threadpool/threadpool.h" -#include "utils/builtins.h" -#include "utils/fmgroids.h" -#include "utils/guc_storage.h" -#include "utils/memutils.h" -#include "utils/plog.h" -#include "utils/postinit.h" -#include "utils/ps_status.h" -#include "utils/rel.h" -#include "utils/rel_gs.h" -#include "utils/relmapper.h" -#include "utils/snapmgr.h" -#include "access/parallel_recovery/page_redo.h" - -#ifdef PGXC -#include "nodes/nodes.h" -#include "pgxc/poolmgr.h" -#endif - -#include "gssignal/gs_signal.h" - -#define ALLOC(t, c) ((t*)selfpalloc0((unsigned)(c) * sizeof(t))) - -static void CheckerModeMain(void); -static void BootstrapModeMain(void); -static void bootstrap_signals(void); -static Form_pg_attribute AllocateAttribute(void); -static Oid gettype(char* type); -static void cleanup(void); - -/* - * Basic information associated with each type. This is used before - * pg_type is filled, so it has to cover the datatypes used as column types - * in the core "bootstrapped" catalogs. - * - * XXX several of these input/output functions do catalog scans - * (e.g., F_REGPROCIN scans pg_proc). this obviously creates some - * order dependencies in the catalog creation process. - */ -struct typinfo { - char name[NAMEDATALEN]; - Oid oid; - Oid elem; - int16 len; - bool byval; - char align; - char storage; - Oid collation; - Oid inproc; - Oid outproc; -}; - -static const struct typinfo TypInfo[] = {{"bool", BOOLOID, 0, 1, true, 'c', 'p', InvalidOid, F_BOOLIN, F_BOOLOUT}, - {"bytea", BYTEAOID, 0, -1, false, 'i', 'x', InvalidOid, F_BYTEAIN, F_BYTEAOUT}, - {"char", CHAROID, 0, 1, true, 'c', 'p', InvalidOid, F_CHARIN, F_CHAROUT}, - {"int1", INT1OID, 0, 1, true, 'c', 'p', InvalidOid, F_INT1IN, F_INT1OUT}, - {"int2", INT2OID, 0, 2, true, 's', 'p', InvalidOid, F_INT2IN, F_INT2OUT}, - {"int4", INT4OID, 0, 4, true, 'i', 'p', InvalidOid, F_INT4IN, F_INT4OUT}, - {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid, F_FLOAT4IN, F_FLOAT4OUT}, - {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', InvalidOid, F_NAMEIN, F_NAMEOUT}, - {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGCLASSIN, F_REGCLASSOUT}, - {"regproc", REGPROCOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGPROCIN, F_REGPROCOUT}, - {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGTYPEIN, F_REGTYPEOUT}, - {"text", TEXTOID, 0, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_TEXTIN, F_TEXTOUT}, - {"oid", OIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_OIDIN, F_OIDOUT}, - {"tid", TIDOID, 0, 6, false, 's', 'p', InvalidOid, F_TIDIN, F_TIDOUT}, - {"xid", XIDOID, 0, 8, FLOAT8PASSBYVAL, 'd', 'p', InvalidOid, F_XIDIN, F_XIDOUT}, - {"xid32", SHORTXIDOID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid, F_XIDIN4, F_XIDOUT4}, - {"cid", CIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_CIDIN, F_CIDOUT}, - {"pg_node_tree", - PGNODETREEOID, - 0, - -1, - false, - 'i', - 'x', - DEFAULT_COLLATION_OID, - F_PG_NODE_TREE_IN, - F_PG_NODE_TREE_OUT}, - {"int2vector", INT2VECTOROID, INT2OID, -1, false, 'i', 'p', InvalidOid, F_INT2VECTORIN, F_INT2VECTOROUT}, - {"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p', InvalidOid, F_OIDVECTORIN, F_OIDVECTOROUT}, - {"_int2", INT2ARRAYOID, INT2OID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, - {"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, - {"_text", 1009, TEXTOID, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_ARRAY_IN, F_ARRAY_OUT}, - {"_oid", 1028, OIDOID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, - {"_char", 1002, CHAROID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, - {"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, - {"raw", RAWOID, 0, -1, false, 'i', 'x', InvalidOid, F_BYTEAIN, F_BYTEAOUT}, - {"oidvector_extend", - OIDVECTOREXTENDOID, - OIDOID, - -1, - false, - 'i', - 'x', - InvalidOid, - F_OIDVECTORIN_EXTEND, - F_OIDVECTOROUT_EXTEND}, - {"int2vector_extend", - INT2VECTOREXTENDOID, - INT2OID, - -1, - false, - 'i', - 'x', - InvalidOid, - F_INT2VECTORIN, - F_INT2VECTOROUT}}; - -static const int n_types = sizeof(TypInfo) / sizeof(struct typinfo); - -struct typmap { /* a hack */ - Oid am_oid; - FormData_pg_type am_typ; -}; - -static THR_LOCAL Datum values[MAXATTR]; /* current row's attribute values */ -static THR_LOCAL bool Nulls[MAXATTR]; - -/* - * At bootstrap time, we first declare all the indices to be built, and - * then build them. The IndexList structure stores enough information - * to allow us to build the indices after they've been declared. - */ -typedef struct _IndexList { - Oid il_heap; - Oid il_ind; - IndexInfo* il_info; - struct _IndexList* il_next; -} IndexList; - -/* - * BootStrapProcessMain - * - * The main entry point for auxiliary processes, such as the bgwriter, - * walwriter, walreceiver, bootstrapper and the shared memory checker code. - * - * This code is here just because of historical reasons. - */ -void BootStrapProcessMain(int argc, char* argv[]) -{ - char* progName = argv[0]; - int flag; - char* userDoption = NULL; - OptParseContext optCtxt; - errno_t errorno = EOK; - - /* - * initialize globals - */ - PostmasterPid = gs_thread_self(); - - t_thrd.proc_cxt.MyProcPid = gs_thread_self(); - - t_thrd.proc_cxt.MyStartTime = time(NULL); - - /* - * Initialize random() for the first time, like PostmasterMain() would. - * In a regular IsUnderPostmaster backend, BackendRun() computes a - * high-entropy seed before any user query. Fewer distinct initial seeds - * can occur here. - */ - srandom((unsigned int)(t_thrd.proc_cxt.MyProcPid ^ (unsigned int)t_thrd.proc_cxt.MyStartTime)); - - t_thrd.proc_cxt.MyProgName = "BootStrap"; - /* - * Fire up essential subsystems: error and memory management - * - * If we are running under the postmaster, this is done already. - */ - if (!IsUnderPostmaster) { - MemoryContextInit(); - init_plog_global_mem(); - } - - /* Compute paths, if we didn't inherit them from postmaster */ - if (my_exec_path[0] == '\0') { - if (find_my_exec(progName, my_exec_path) < 0) - ereport(FATAL, (errmsg("%s: could not locate my own executable path", progName))); - } - - /* - * process command arguments - */ - /* Set defaults, to be overriden by explicit options below */ - if (!IsUnderPostmaster) { - InitializeGUCOptions(); - } - - /* Ignore the initial --boot argument, if present */ - if (argc > 1 && strcmp(argv[1], "--boot") == 0) { - argv++; - argc--; - } - - /* If no -x argument, we are a CheckerProcess */ - t_thrd.bootstrap_cxt.MyAuxProcType = CheckerProcess; - - initOptParseContext(&optCtxt); - while ((flag = getopt_r(argc, argv, "B:c:d:D:Fr:x:g:-:", &optCtxt)) != -1) { - switch (flag) { - case 'B': - SetConfigOption("shared_buffers", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV); - break; - case 'D': - userDoption = optCtxt.optarg; - break; - case 'd': { - int debugStrLen = strlen("debug") + strlen(optCtxt.optarg) + 1; - /* Turn on debugging for the bootstrap process. */ - char* debugstr = (char*)palloc(debugStrLen); - - errorno = snprintf_s(debugstr, debugStrLen, debugStrLen - 1, "debug%s", optCtxt.optarg); - securec_check_ss(errorno, "\0", "\0"); - SetConfigOption("log_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV); - SetConfigOption("client_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV); - pfree(debugstr); - } break; - case 'F': - SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV); - break; - case 'g': - SetConfigOption("xlog_file_path", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV); - break; - case 'r': - errorno = strcpy_s(t_thrd.proc_cxt.OutputFileName, MAXPGPATH, optCtxt.optarg); - securec_check(errorno, "\0", "\0"); - break; - case 'x': - t_thrd.bootstrap_cxt.MyAuxProcType = (AuxProcType)atoi(optCtxt.optarg); - break; - case 'c': - case '-': { - char* name = NULL; - char* value = NULL; - - ParseLongOption(optCtxt.optarg, &name, &value); - if (value == NULL) { - if (flag == '-') - ereport( - ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("--%s requires a value", optCtxt.optarg))); - else - ereport( - ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("-c %s requires a value", optCtxt.optarg))); - } - - SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV); - pfree(name); - if (value != NULL) - pfree(value); - break; - } - default: - write_stderr("Try \"%s --help\" for more information.\n", progName); - proc_exit(1); - break; - } - } - - if (argc != optCtxt.optind) { - write_stderr("%s: invalid command-line arguments\n", progName); - proc_exit(1); - } - - /* Acquire configuration parameters, unless inherited from postmaster */ - if (!IsUnderPostmaster) { - if (!SelectConfigFiles(userDoption, progName)) { - proc_exit(1); - } - InitializeNumLwLockPartitions(); - } - g_instance.global_sysdbcache.Init(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); - CreateLocalSysDBCache(); - - /* Validate we have been given a reasonable-looking t_thrd.proc_cxt.DataDir */ - Assert(t_thrd.proc_cxt.DataDir); - ValidatePgVersion(t_thrd.proc_cxt.DataDir); - - /* Change into t_thrd.proc_cxt.DataDir (if under postmaster, should be done already) */ - if (!IsUnderPostmaster) - ChangeToDataDir(); - - /* If standalone, create lockfile for data directory */ - if (!IsUnderPostmaster) - CreateDataDirLockFile(false); - - SetProcessingMode(BootstrapProcessing); - u_sess->attr.attr_common.IgnoreSystemIndexes = true; - - BaseInit(); - - pgstat_initialize(); - pgstat_bestart(); - if (!IsUnderPostmaster) { - ShareStorageInit(); - } - /* - * XLOG operations - */ - SetProcessingMode(NormalProcessing); - - switch (t_thrd.bootstrap_cxt.MyAuxProcType) { - case CheckerProcess: - /* don't set signals, they're useless here */ - CheckerModeMain(); - proc_exit(1); /* should never return */ - - case BootstrapProcess: - bootstrap_signals(); - BootStrapXLOG(); - MemoryContextUnSeal(t_thrd.top_mem_cxt); - BootstrapModeMain(); - MemoryContextSeal(t_thrd.top_mem_cxt); - proc_exit(1); /* should never return */ - - default: - ereport(PANIC, (errmsg("unrecognized process type: %d", (int)t_thrd.bootstrap_cxt.MyAuxProcType))); - proc_exit(1); - } -} - -/* - * In shared memory checker mode, all we really want to do is create shared - * memory and semaphores (just to prove we can do it with the current GUC - * settings). Since, in fact, that was already done by BaseInit(), - * we have nothing more to do here. - */ -static void CheckerModeMain(void) -{ - proc_exit(0); -} - -/* - * The main entry point for running the backend in bootstrap mode - * - * The bootstrap mode is used to initialize the template database. - * The bootstrap backend doesn't speak SQL, but instead expects - * commands in a special bootstrap language. - */ -static void BootstrapModeMain(void) -{ - int i; - - Assert(!IsUnderPostmaster); - - SetProcessingMode(BootstrapProcessing); - - /* - * Do backend-like initialization for bootstrap mode - */ - InitProcess(); - - t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(NULL, InvalidOid, NULL); - t_thrd.proc_cxt.PostInit->InitBootstrap(); - - /* Initialize stuff for bootstrap-file processing */ - for (i = 0; i < MAXATTR; i++) { - t_thrd.bootstrap_cxt.attrtypes[i] = NULL; - Nulls[i] = false; - } - - /* - * Process bootstrap input. - */ - boot_yyparse(); - - /* - * We should now know about all mapped relations, so it's okay to write - * out the initial relation mapping files. - */ - RelationMapFinishBootstrap(); - - /* Clean up and exit */ - cleanup(); - proc_exit(0); -} - -/* ---------------------------------------------------------------- - * misc functions - * ---------------------------------------------------------------- - */ -/* - * Set up signal handling for a bootstrap process - */ -static void bootstrap_signals(void) -{ - if (IsUnderPostmaster) { - /* - * Properly accept or ignore signals the postmaster might send us - */ - (void)gspqsignal(SIGHUP, SIG_IGN); - (void)gspqsignal(SIGINT, SIG_IGN); /* ignore query-cancel */ - (void)gspqsignal(SIGTERM, die); - (void)gspqsignal(SIGQUIT, quickdie); - (void)gspqsignal(SIGALRM, SIG_IGN); - (void)gspqsignal(SIGPIPE, SIG_IGN); - (void)gspqsignal(SIGUSR1, SIG_IGN); - (void)gspqsignal(SIGUSR2, SIG_IGN); - - /* - * Reset some signals that are accepted by postmaster but not here - */ - (void)gspqsignal(SIGCHLD, SIG_DFL); - (void)gspqsignal(SIGTTIN, SIG_DFL); - (void)gspqsignal(SIGTTOU, SIG_DFL); - (void)gspqsignal(SIGCONT, SIG_DFL); - (void)gspqsignal(SIGWINCH, SIG_DFL); - - /* - * Unblock signals (they were blocked when the postmaster forked us) - */ - gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); - (void)gs_signal_unblock_sigusr2(); - - } else { - /* Set up appropriately for interactive use */ - (void)gspqsignal(SIGHUP, die); - (void)gspqsignal(SIGINT, die); - (void)gspqsignal(SIGTERM, die); - (void)gspqsignal(SIGQUIT, die); - (void)gs_signal_unblock_sigusr2(); - } -} - -/* ---------------------------------------------------------------- - * MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS - * ---------------------------------------------------------------- - */ -/* ---------------- - * boot_openrel - * ---------------- - */ -void boot_openrel(char* relname) -{ - int i; - struct typmap** app; - Relation rel; - TableScanDesc scan; - HeapTuple tup; - errno_t rc; - - if (strlen(relname) >= NAMEDATALEN) - relname[NAMEDATALEN - 1] = '\0'; - - if (t_thrd.bootstrap_cxt.Typ == NULL) { - /* We can now load the pg_type data */ - rel = heap_open(TypeRelationId, NoLock); - scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); - i = 0; - - while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) - ++i; - tableam_scan_end(scan); - app = t_thrd.bootstrap_cxt.Typ = ALLOC(struct typmap*, i + 1); - while (i-- > 0) - *app++ = ALLOC(struct typmap, 1); - *app = NULL; - scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); - app = t_thrd.bootstrap_cxt.Typ; - while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) { - (*app)->am_oid = HeapTupleGetOid(tup); - rc = - memcpy_s((char*)&(*app)->am_typ, sizeof((*app)->am_typ), (char*)GETSTRUCT(tup), sizeof((*app)->am_typ)); - securec_check(rc, "\0", "\0"); - app++; - } - tableam_scan_end(scan); - heap_close(rel, NoLock); - } - - if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) - closerel(NULL); - - ereport(DEBUG4, (errmsg("open relation %s, attrsize %d", relname, (int)ATTRIBUTE_FIXED_PART_SIZE))); - - t_thrd.bootstrap_cxt.boot_reldesc = heap_openrv(makeRangeVar(NULL, relname, -1), NoLock); - t_thrd.bootstrap_cxt.numattr = RelationGetNumberOfAttributes(t_thrd.bootstrap_cxt.boot_reldesc); - for (i = 0; i < t_thrd.bootstrap_cxt.numattr; i++) { - if (t_thrd.bootstrap_cxt.attrtypes[i] == NULL) - t_thrd.bootstrap_cxt.attrtypes[i] = AllocateAttribute(); - rc = memmove_s((char*)t_thrd.bootstrap_cxt.attrtypes[i], - ATTRIBUTE_FIXED_PART_SIZE, - (char*)t_thrd.bootstrap_cxt.boot_reldesc->rd_att->attrs[i], - ATTRIBUTE_FIXED_PART_SIZE); - securec_check(rc, "\0", "\0"); - - { - Form_pg_attribute at = t_thrd.bootstrap_cxt.attrtypes[i]; - - ereport(DEBUG4, - (errmsg("create attribute %d name %s len %d num %d type %u", - i, - NameStr(at->attname), - at->attlen, - at->attnum, - at->atttypid))); - } - } -} - -/* ---------------- - * closerel - * ---------------- - */ -void closerel(char* name) -{ - if (name != NULL) { - if (t_thrd.bootstrap_cxt.boot_reldesc) { - if (strcmp(RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc), name) != 0) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("close of %s when %s was expected", - name, - RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc)))); - } else - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("close of %s before any relation was opened", name))); - } - - if (t_thrd.bootstrap_cxt.boot_reldesc == NULL) - ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("no open relation to close"))); - else { - ereport(DEBUG4, (errmsg("close relation %s", RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc)))); - heap_close(t_thrd.bootstrap_cxt.boot_reldesc, NoLock); - t_thrd.bootstrap_cxt.boot_reldesc = NULL; - } -} - -/* -* fix roluseft ,rolmonitoradmin, roloperatoradmin and rolpolicyadmin column of pg_authid to notnull -*/ -static void fix_attr_notnull(const char* name, int attnum) -{ - if (strncmp(name, "roluseft", strlen("roluseft")) == 0 && strlen(name) == strlen("roluseft")) { - t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; - } - if (strncmp(name, "rolmonitoradmin", strlen("rolmonitoradmin")) == 0 && strlen(name) == strlen("rolmonitoradmin")) { - t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; - } - if (strncmp(name, "roloperatoradmin", strlen("roloperatoradmin")) == 0 && - strlen(name) == strlen("roloperatoradmin")) { - t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; - } - if (strncmp(name, "rolpolicyadmin", strlen("rolpolicyadmin")) == 0 && strlen(name) == strlen("rolpolicyadmin")) { - t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; - } -} - -/* ---------------- - * DEFINEATTR() - * - * define a pair - * if there are n fields in a relation to be created, this routine - * will be called n times - * ---------------- - */ -void DefineAttr(const char* name, char* type, int attnum) -{ - Oid typeoid; - - if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) { - ereport(WARNING, (errmsg("no open relations allowed with CREATE command"))); - closerel(NULL); - } - - if (t_thrd.bootstrap_cxt.attrtypes[attnum] == NULL) - t_thrd.bootstrap_cxt.attrtypes[attnum] = AllocateAttribute(); - MemSet(t_thrd.bootstrap_cxt.attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE); - - (void)namestrcpy(&t_thrd.bootstrap_cxt.attrtypes[attnum]->attname, name); - ereport(DEBUG4, (errmsg("column %s %s", NameStr(t_thrd.bootstrap_cxt.attrtypes[attnum]->attname), type))); - t_thrd.bootstrap_cxt.attrtypes[attnum]->attnum = attnum + 1; /* fillatt */ - - typeoid = gettype(type); - - if (t_thrd.bootstrap_cxt.Typ != NULL) { - t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypid = t_thrd.bootstrap_cxt.Ap->am_oid; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen = t_thrd.bootstrap_cxt.Ap->am_typ.typlen; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attbyval = t_thrd.bootstrap_cxt.Ap->am_typ.typbyval; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attstorage = t_thrd.bootstrap_cxt.Ap->am_typ.typstorage; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attalign = t_thrd.bootstrap_cxt.Ap->am_typ.typalign; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attcollation = t_thrd.bootstrap_cxt.Ap->am_typ.typcollation; - /* if an array type, assume 1-dimensional attribute */ - if (t_thrd.bootstrap_cxt.Ap->am_typ.typelem != InvalidOid && t_thrd.bootstrap_cxt.Ap->am_typ.typlen < 0) - t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 1; - else - t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 0; - } else { - t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypid = TypInfo[typeoid].oid; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen = TypInfo[typeoid].len; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attbyval = TypInfo[typeoid].byval; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attstorage = TypInfo[typeoid].storage; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attalign = TypInfo[typeoid].align; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attcollation = TypInfo[typeoid].collation; - /* if an array type, assume 1-dimensional attribute */ - if (TypInfo[typeoid].elem != InvalidOid && t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen < 0) - t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 1; - else - t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 0; - } - - t_thrd.bootstrap_cxt.attrtypes[attnum]->attstattarget = -1; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attcacheoff = -1; - t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypmod = -1; - t_thrd.bootstrap_cxt.attrtypes[attnum]->attislocal = true; - - /* - * Mark as "not null" if type is fixed-width and prior columns are too. - * This corresponds to case where column can be accessed directly via C - * struct declaration. - * - * oidvector and int2vector are also treated as not-nullable, even though - * they are no longer fixed-width. - */ -#define MARKNOTNULL(att) ((att)->attlen > 0 || (att)->atttypid == OIDVECTOROID || (att)->atttypid == INT2VECTOROID) - - if (MARKNOTNULL(t_thrd.bootstrap_cxt.attrtypes[attnum])) { - int i; - - for (i = 0; i < attnum; i++) { - if (!MARKNOTNULL(t_thrd.bootstrap_cxt.attrtypes[i])) - break; - } - if (i == attnum) - t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; - } - - // fix partkey/intervaltablespace/intspnum columns of pg_partition to nullable - if (strcmp(name, "partkey") == 0 || strcmp(name, "intervaltablespace") == 0 || strcmp(name, "intspnum") == 0) { - t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = false; - } - - // fix roluseft ,rolmonitoradmin, roloperatoradmin and rolpolicyadmin column of pg_authid to notnull - fix_attr_notnull(name, attnum); - -} - -/* ---------------- - * InsertOneTuple - * - * If objectid is not zero, it is a specific OID to assign to the tuple. - * Otherwise, an OID will be assigned (if necessary) by heap_insert. - * ---------------- - */ -void InsertOneTuple(Oid objectid) -{ - HeapTuple tuple; - TupleDesc tupDesc; - int i; - - ereport(DEBUG4, (errmsg("inserting row oid %u, %d columns", objectid, t_thrd.bootstrap_cxt.numattr))); - - if (IsBootingPgProc(t_thrd.bootstrap_cxt.boot_reldesc)) { - ereport(FATAL, (errmsg("Built-in functions should not be added into pg_proc"))); - } - tupDesc = CreateTupleDesc(t_thrd.bootstrap_cxt.numattr, - RelationGetForm(t_thrd.bootstrap_cxt.boot_reldesc)->relhasoids, - t_thrd.bootstrap_cxt.attrtypes, - t_thrd.bootstrap_cxt.boot_reldesc->rd_tam_type); - tuple = (HeapTuple) tableam_tops_form_tuple(tupDesc, values, Nulls, HEAP_TUPLE); - if (objectid != (Oid)0) - HeapTupleSetOid(tuple, objectid); - pfree(tupDesc); /* just free's tupDesc, not the attrtypes */ - - (void)simple_heap_insert(t_thrd.bootstrap_cxt.boot_reldesc, tuple); - tableam_tops_free_tuple(tuple); - ereport(DEBUG4, (errmsg("row inserted"))); - - /* - * Reset null markers for next tuple - */ - for (i = 0; i < t_thrd.bootstrap_cxt.numattr; i++) - Nulls[i] = false; -} - -/* ---------------- - * InsertOneValue - * ---------------- - */ -void InsertOneValue(char* value, int i) -{ - Oid typoid; - int16 typlen; - bool typbyval = false; - char typalign; - char typdelim; - Oid typioparam; - Oid typinput; - Oid typoutput; - char* prt = NULL; - - AssertArg(i >= 0 && i < MAXATTR); - - ereport(DEBUG4, (errmsg("inserting column %d value \"%s\"", i, value))); - - typoid = t_thrd.bootstrap_cxt.boot_reldesc->rd_att->attrs[i]->atttypid; - - boot_get_type_io_data(typoid, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typinput, &typoutput); - - values[i] = OidInputFunctionCall(typinput, value, typioparam, -1); - prt = OidOutputFunctionCall(typoutput, values[i]); - ereport(DEBUG4, (errmsg("inserted -> %s", prt))); - pfree(prt); -} - -/* ---------------- - * InsertOneNull - * ---------------- - */ -void InsertOneNull(int i) -{ - ereport(DEBUG4, (errmsg("inserting column %d NULL", i))); - Assert(i >= 0 && i < MAXATTR); - values[i] = PointerGetDatum(NULL); - Nulls[i] = true; -} - -/* ---------------- - * cleanup - * ---------------- - */ -static void cleanup(void) -{ - if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) - closerel(NULL); -} - -/* ---------------- - * gettype - * - * NB: this is really ugly; it will return an integer index into TypInfo[], - * and not an OID at all, until the first reference to a type not known in - * TypInfo[]. At that point it will read and cache pg_type in the Typ array, - * and subsequently return a real OID (and set the global pointer Ap to - * point at the found row in Typ). So caller must check whether Typ is - * still NULL to determine what the return value is! - * ---------------- - */ -static Oid gettype(char* type) -{ - int i; - Relation rel; - TableScanDesc scan; - HeapTuple tup; - struct typmap** app; - errno_t rc; - - if (t_thrd.bootstrap_cxt.Typ != NULL) { - for (app = t_thrd.bootstrap_cxt.Typ; *app != NULL; app++) { - if (strncmp(NameStr((*app)->am_typ.typname), type, NAMEDATALEN) == 0) { - t_thrd.bootstrap_cxt.Ap = *app; - return (*app)->am_oid; - } - } - } else { - for (i = 0; i < n_types; i++) { - if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0) - return i; - } - ereport(DEBUG4, (errmsg("external type: %s", type))); - rel = heap_open(TypeRelationId, NoLock); - scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); - i = 0; - while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) - ++i; - tableam_scan_end(scan); - app = t_thrd.bootstrap_cxt.Typ = ALLOC(struct typmap*, i + 1); - while (i-- > 0) - *app++ = ALLOC(struct typmap, 1); - *app = NULL; - scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); - app = t_thrd.bootstrap_cxt.Typ; - while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) { - (*app)->am_oid = HeapTupleGetOid(tup); - rc = memmove_s( - (char*)&(*app++)->am_typ, sizeof((*app)->am_typ), (char*)GETSTRUCT(tup), sizeof((*app)->am_typ)); - securec_check(rc, "\0", "\0"); - } - tableam_scan_end(scan); - heap_close(rel, NoLock); - return gettype(type); - } - ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unrecognized type \"%s\"", type))); - /* not reached, here to make compiler happy */ - return 0; -} - -/* ---------------- - * boot_get_type_io_data - * - * Obtain type I/O information at bootstrap time. This intentionally has - * almost the same API as lsyscache.c's get_type_io_data, except that - * we only support obtaining the typinput and typoutput routines, not - * the binary I/O routines. It is exported so that array_in and array_out - * can be made to work during early bootstrap. - * ---------------- - */ -void boot_get_type_io_data(Oid typid, int16* typlen, bool* typbyval, char* typalign, char* typdelim, Oid* typioparam, - Oid* typinput, Oid* typoutput) -{ - if (t_thrd.bootstrap_cxt.Typ != NULL) { - /* We have the boot-time contents of pg_type, so use it */ - struct typmap** app; - struct typmap* ap = NULL; - - app = t_thrd.bootstrap_cxt.Typ; - while (*app && (*app)->am_oid != typid) - ++app; - ap = *app; - if (ap == NULL) - ereport( - ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("type OID %u not found in Typ list", typid))); - - *typlen = ap->am_typ.typlen; - *typbyval = ap->am_typ.typbyval; - *typalign = ap->am_typ.typalign; - *typdelim = ap->am_typ.typdelim; - - /* XXX this logic must match getTypeIOParam() */ - if (OidIsValid(ap->am_typ.typelem)) - *typioparam = ap->am_typ.typelem; - else - *typioparam = typid; - - *typinput = ap->am_typ.typinput; - *typoutput = ap->am_typ.typoutput; - } else { - /* We don't have pg_type yet, so use the hard-wired TypInfo array */ - int typeindex; - - for (typeindex = 0; typeindex < n_types; typeindex++) { - if (TypInfo[typeindex].oid == typid) - break; - } - if (typeindex >= n_types) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("type OID %u not found in TypInfo", typid))); - - *typlen = TypInfo[typeindex].len; - *typbyval = TypInfo[typeindex].byval; - *typalign = TypInfo[typeindex].align; - /* We assume typdelim is ',' for all boot-time types */ - *typdelim = ','; - - /* XXX this logic must match getTypeIOParam() */ - if (OidIsValid(TypInfo[typeindex].elem)) - *typioparam = TypInfo[typeindex].elem; - else - *typioparam = typid; - - *typinput = TypInfo[typeindex].inproc; - *typoutput = TypInfo[typeindex].outproc; - } -} - -/* ---------------- - * AllocateAttribute - * - * Note: bootstrap never sets any per-column ACLs, so we only need - * ATTRIBUTE_FIXED_PART_SIZE space per attribute. - * ---------------- - */ -static Form_pg_attribute AllocateAttribute(void) -{ - Form_pg_attribute attribute = (Form_pg_attribute)MemoryContextAlloc( - SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), ATTRIBUTE_FIXED_PART_SIZE); - - if (!PointerIsValid(attribute)) - ereport(FATAL, (errmsg("out of memory"))); - MemSet(attribute, 0, ATTRIBUTE_FIXED_PART_SIZE); - - return attribute; -} - -/* ---------------- - * MapArrayTypeName - * XXX arrays of "basetype" are always "_basetype". - * this is an evil hack inherited from rel. 3.1. - * XXX array dimension is thrown away because we - * don't support fixed-dimension arrays. again, - * sickness from 3.1. - * - * the string passed in must have a '[' character in it - * - * the string returned is a pointer to static storage and should NOT - * be freed by the CALLER. - * ---------------- - */ -const char* MapArrayTypeName(const char* s) -{ - int i; - int j; - - if (s == NULL || s[0] == '\0') - return s; - - j = 1; - t_thrd.bootstrap_cxt.newStr[0] = '_'; - for (i = 0; i < NAMEDATALEN - 1 && s[i] != '['; i++, j++) - t_thrd.bootstrap_cxt.newStr[j] = s[i]; - - t_thrd.bootstrap_cxt.newStr[j] = '\0'; - - return t_thrd.bootstrap_cxt.newStr; -} - -/* - * index_register() -- record an index that has been set up for building - * later. - * - * At bootstrap time, we define a bunch of indexes on system catalogs. - * We postpone actually building the indexes until just before we're - * finished with initialization, however. This is because the indexes - * themselves have catalog entries, and those have to be included in the - * indexes on those catalogs. Doing it in two phases is the simplest - * way of making sure the indexes have the right contents at the end. - */ -void index_register(Oid heap, Oid ind, IndexInfo* indexInfo) -{ - IndexList* newind = NULL; - MemoryContext oldcxt; - errno_t rc; - - /* - * XXX mao 10/31/92 -- don't gc index reldescs, associated info at - * bootstrap time. we'll declare the indexes now, but want to create them - * later. - */ - if (t_thrd.bootstrap_cxt.nogc == NULL) - t_thrd.bootstrap_cxt.nogc = AllocSetContextCreate( - NULL, "BootstrapNoGC", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - - oldcxt = MemoryContextSwitchTo(t_thrd.bootstrap_cxt.nogc); - - newind = (IndexList*)palloc(sizeof(IndexList)); - newind->il_heap = heap; - newind->il_ind = ind; - newind->il_info = (IndexInfo*)palloc(sizeof(IndexInfo)); - - rc = memcpy_s(newind->il_info, sizeof(IndexInfo), indexInfo, sizeof(IndexInfo)); - securec_check(rc, "\0", "\0"); - /* expressions will likely be null, but may as well copy it */ - newind->il_info->ii_Expressions = (List*)copyObject(indexInfo->ii_Expressions); - newind->il_info->ii_ExpressionsState = NIL; - /* predicate will likely be null, but may as well copy it */ - newind->il_info->ii_Predicate = (List*)copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; - /* no exclusion constraints at bootstrap time, so no need to copy */ - Assert(indexInfo->ii_ExclusionOps == NULL); - Assert(indexInfo->ii_ExclusionProcs == NULL); - Assert(indexInfo->ii_ExclusionStrats == NULL); - - newind->il_next = t_thrd.bootstrap_cxt.ILHead; - t_thrd.bootstrap_cxt.ILHead = newind; - - (void)MemoryContextSwitchTo(oldcxt); -} - -/* - * build_indices -- fill in all the indexes registered earlier - */ -void build_indices(void) -{ - for (; t_thrd.bootstrap_cxt.ILHead != NULL; t_thrd.bootstrap_cxt.ILHead = t_thrd.bootstrap_cxt.ILHead->il_next) { - Relation heap; - Relation ind; - - /* need not bother with locks during bootstrap */ - heap = heap_open(t_thrd.bootstrap_cxt.ILHead->il_heap, NoLock); - ind = index_open(t_thrd.bootstrap_cxt.ILHead->il_ind, NoLock); - index_build( - heap, NULL, ind, NULL, t_thrd.bootstrap_cxt.ILHead->il_info, false, false, INDEX_CREATE_NONE_PARTITION); - - index_close(ind, NoLock); - heap_close(heap, NoLock); - } -} -- 2.34.1 From 0c7d7a750ee6691484787297489679331eb4098e Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:12:45 +0800 Subject: [PATCH 16/56] ADD file via upload --- bootstrap.cpp | 1137 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1137 insertions(+) create mode 100644 bootstrap.cpp diff --git a/bootstrap.cpp b/bootstrap.cpp new file mode 100644 index 000000000..f35dfe39d --- /dev/null +++ b/bootstrap.cpp @@ -0,0 +1,1137 @@ +/* ------------------------------------------------------------------------- + * + * bootstrap.c + * routines to support running openGauss in 'bootstrap' mode + * bootstrap mode is used to create the initial template database + * + * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2012 Postgres-XC Development Group + * + * IDENTIFICATION + * src/backend/bootstrap/bootstrap.c + * + * ------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "knl/knl_variable.h" +#include "pgstat.h" +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include "access/tableam.h" +#include "bootstrap/bootstrap.h" +#include "catalog/index.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "libpq/pqsignal.h" +#include "miscadmin.h" +#include "pgstat.h" +#include "nodes/makefuncs.h" +#include "postmaster/aiocompleter.h" +#include "postmaster/bgwriter.h" +#include "postmaster/pagewriter.h" +#include "postmaster/cbmwriter.h" +#include "postmaster/startup.h" +#include "postmaster/twophasecleaner.h" +#include "postmaster/licensechecker.h" +#include "postmaster/walwriter.h" +#include "postmaster/lwlockmonitor.h" +#include "replication/walreceiver.h" +#include "replication/datareceiver.h" +#include "storage/buf/bufmgr.h" +#include "storage/ipc.h" +#include "storage/proc.h" +#include "tcop/tcopprot.h" +#include "threadpool/threadpool.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/guc_storage.h" +#include "utils/memutils.h" +#include "utils/plog.h" +#include "utils/postinit.h" +#include "utils/ps_status.h" +#include "utils/rel.h" +#include "utils/rel_gs.h" +#include "utils/relmapper.h" +#include "utils/snapmgr.h" +#include "access/parallel_recovery/page_redo.h" + +#ifdef PGXC +#include "nodes/nodes.h" +#include "pgxc/poolmgr.h" +#endif + +#include "gssignal/gs_signal.h" + +#define ALLOC(t, c) ((t*)selfpalloc0((unsigned)(c) * sizeof(t))) + +static void CheckerModeMain(void); +static void BootstrapModeMain(void); +static void bootstrap_signals(void); +static Form_pg_attribute AllocateAttribute(void); +static Oid gettype(char* type); +static void cleanup(void); + +/* + * Basic information associated with each type. This is used before + * pg_type is filled, so it has to cover the datatypes used as column types + * in the core "bootstrapped" catalogs. + * + * XXX several of these input/output functions do catalog scans + * (e.g., F_REGPROCIN scans pg_proc). this obviously creates some + * order dependencies in the catalog creation process. + */ +struct typinfo { + char name[NAMEDATALEN]; + Oid oid; + Oid elem; + int16 len; + bool byval; + char align; + char storage; + Oid collation; + Oid inproc; + Oid outproc; +}; + +static const struct typinfo TypInfo[] = {{"bool", BOOLOID, 0, 1, true, 'c', 'p', InvalidOid, F_BOOLIN, F_BOOLOUT}, + {"bytea", BYTEAOID, 0, -1, false, 'i', 'x', InvalidOid, F_BYTEAIN, F_BYTEAOUT}, + {"char", CHAROID, 0, 1, true, 'c', 'p', InvalidOid, F_CHARIN, F_CHAROUT}, + {"int1", INT1OID, 0, 1, true, 'c', 'p', InvalidOid, F_INT1IN, F_INT1OUT}, + {"int2", INT2OID, 0, 2, true, 's', 'p', InvalidOid, F_INT2IN, F_INT2OUT}, + {"int4", INT4OID, 0, 4, true, 'i', 'p', InvalidOid, F_INT4IN, F_INT4OUT}, + {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid, F_FLOAT4IN, F_FLOAT4OUT}, + {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', InvalidOid, F_NAMEIN, F_NAMEOUT}, + {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGCLASSIN, F_REGCLASSOUT}, + {"regproc", REGPROCOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGPROCIN, F_REGPROCOUT}, + {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGTYPEIN, F_REGTYPEOUT}, + {"text", TEXTOID, 0, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_TEXTIN, F_TEXTOUT}, + {"oid", OIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_OIDIN, F_OIDOUT}, + {"tid", TIDOID, 0, 6, false, 's', 'p', InvalidOid, F_TIDIN, F_TIDOUT}, + {"xid", XIDOID, 0, 8, FLOAT8PASSBYVAL, 'd', 'p', InvalidOid, F_XIDIN, F_XIDOUT}, + {"xid32", SHORTXIDOID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid, F_XIDIN4, F_XIDOUT4}, + {"cid", CIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_CIDIN, F_CIDOUT}, + {"pg_node_tree", + PGNODETREEOID, + 0, + -1, + false, + 'i', + 'x', + DEFAULT_COLLATION_OID, + F_PG_NODE_TREE_IN, + F_PG_NODE_TREE_OUT}, + {"int2vector", INT2VECTOROID, INT2OID, -1, false, 'i', 'p', InvalidOid, F_INT2VECTORIN, F_INT2VECTOROUT}, + {"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p', InvalidOid, F_OIDVECTORIN, F_OIDVECTOROUT}, + {"_int2", INT2ARRAYOID, INT2OID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_text", 1009, TEXTOID, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_ARRAY_IN, F_ARRAY_OUT}, + {"_oid", 1028, OIDOID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_char", 1002, CHAROID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"raw", RAWOID, 0, -1, false, 'i', 'x', InvalidOid, F_BYTEAIN, F_BYTEAOUT}, + {"oidvector_extend", + OIDVECTOREXTENDOID, + OIDOID, + -1, + false, + 'i', + 'x', + InvalidOid, + F_OIDVECTORIN_EXTEND, + F_OIDVECTOROUT_EXTEND}, + {"int2vector_extend", + INT2VECTOREXTENDOID, + INT2OID, + -1, + false, + 'i', + 'x', + InvalidOid, + F_INT2VECTORIN, + F_INT2VECTOROUT}}; + +static const int n_types = sizeof(TypInfo) / sizeof(struct typinfo); + +struct typmap { /* a hack */ + Oid am_oid; + FormData_pg_type am_typ; +}; + +static THR_LOCAL Datum values[MAXATTR]; /* current row's attribute values */ +static THR_LOCAL bool Nulls[MAXATTR]; + +/* + * At bootstrap time, we first declare all the indices to be built, and + * then build them. The IndexList structure stores enough information + * to allow us to build the indices after they've been declared. + */ +typedef struct _IndexList { + Oid il_heap; + Oid il_ind; + IndexInfo* il_info; + struct _IndexList* il_next; +} IndexList; + +/* + * BootStrapProcessMain + * + * The main entry point for auxiliary processes, such as the bgwriter, + * walwriter, walreceiver, bootstrapper and the shared memory checker code. + * + * This code is here just because of historical reasons. + */ +//这是一个主要的引导启动进程。它包含以下主要步骤: +//初始化全局变量,设置进程ID(PostmasterPid)和启动时间(MyStartTime)。 +//使用进程ID和启动时间作为种子初始化随机数。 +//初始化错误和内存管理子系统。 +//初始化全局配置选项。 +//处理命令行参数。根据参数设置相应的配置选项。 +//验证并设置数据目录。 +//创建数据目录的锁文件。 +//设置处理模式为BootstrapProcessing。 +//初始化基本的后台进程。 +//初始化统计信息收集。 +//根据进程类型执行相应的操作。 +//如果进程类型是CheckerProcess,则执行CheckerModeMain()函数,并退出进程。 +//如果进程类型是BootstrapProcess,则设置信号处理函数,执行BootStrapXLOG()函数,然后执行BootstrapModeMain()函数,并退出进程。 +//如果进程类型未被识别,则触发PANIC错误,并退出进程。 +void BootStrapProcessMain(int argc, char* argv[]) +{ + char* progName = argv[0]; + int flag; + char* userDoption = NULL; + OptParseContext optCtxt; + errno_t errorno = EOK; + + /* + * initialize globals + */ + PostmasterPid = gs_thread_self(); + + t_thrd.proc_cxt.MyProcPid = gs_thread_self(); + + t_thrd.proc_cxt.MyStartTime = time(NULL); + + /* + * Initialize random() for the first time, like PostmasterMain() would. + * In a regular IsUnderPostmaster backend, BackendRun() computes a + * high-entropy seed before any user query. Fewer distinct initial seeds + * can occur here. + */ + srandom((unsigned int)(t_thrd.proc_cxt.MyProcPid ^ (unsigned int)t_thrd.proc_cxt.MyStartTime)); + + t_thrd.proc_cxt.MyProgName = "BootStrap"; + /* + * Fire up essential subsystems: error and memory management + * + * If we are running under the postmaster, this is done already. + */ + if (!IsUnderPostmaster) { + MemoryContextInit(); + init_plog_global_mem(); + } + + /* Compute paths, if we didn't inherit them from postmaster */ + if (my_exec_path[0] == '\0') { + if (find_my_exec(progName, my_exec_path) < 0) + ereport(FATAL, (errmsg("%s: could not locate my own executable path", progName))); + } + + /* + * process command arguments + */ + /* Set defaults, to be overriden by explicit options below */ + if (!IsUnderPostmaster) { + InitializeGUCOptions(); + } + + /* Ignore the initial --boot argument, if present */ + if (argc > 1 && strcmp(argv[1], "--boot") == 0) { + argv++; + argc--; + } + + /* If no -x argument, we are a CheckerProcess */ + t_thrd.bootstrap_cxt.MyAuxProcType = CheckerProcess; + + initOptParseContext(&optCtxt); + while ((flag = getopt_r(argc, argv, "B:c:d:D:Fr:x:g:-:", &optCtxt)) != -1) { + switch (flag) { + case 'B': + SetConfigOption("shared_buffers", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV); + break; + case 'D': + userDoption = optCtxt.optarg; + break; + case 'd': { + int debugStrLen = strlen("debug") + strlen(optCtxt.optarg) + 1; + /* Turn on debugging for the bootstrap process. */ + char* debugstr = (char*)palloc(debugStrLen); + + errorno = snprintf_s(debugstr, debugStrLen, debugStrLen - 1, "debug%s", optCtxt.optarg); + securec_check_ss(errorno, "\0", "\0"); + SetConfigOption("log_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV); + SetConfigOption("client_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV); + pfree(debugstr); + } break; + case 'F': + SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV); + break; + case 'g': + SetConfigOption("xlog_file_path", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV); + break; + case 'r': + errorno = strcpy_s(t_thrd.proc_cxt.OutputFileName, MAXPGPATH, optCtxt.optarg); + securec_check(errorno, "\0", "\0"); + break; + case 'x': + t_thrd.bootstrap_cxt.MyAuxProcType = (AuxProcType)atoi(optCtxt.optarg); + break; + case 'c': + case '-': { + char* name = NULL; + char* value = NULL; + + ParseLongOption(optCtxt.optarg, &name, &value); + if (value == NULL) { + if (flag == '-') + ereport( + ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("--%s requires a value", optCtxt.optarg))); + else + ereport( + ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("-c %s requires a value", optCtxt.optarg))); + } + + SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV); + pfree(name); + if (value != NULL) + pfree(value); + break; + } + default: + write_stderr("Try \"%s --help\" for more information.\n", progName); + proc_exit(1); + break; + } + } + + if (argc != optCtxt.optind) { + write_stderr("%s: invalid command-line arguments\n", progName); + proc_exit(1); + } + + /* Acquire configuration parameters, unless inherited from postmaster */ + if (!IsUnderPostmaster) { + if (!SelectConfigFiles(userDoption, progName)) { + proc_exit(1); + } + InitializeNumLwLockPartitions(); + } + g_instance.global_sysdbcache.Init(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); + CreateLocalSysDBCache(); + + /* Validate we have been given a reasonable-looking t_thrd.proc_cxt.DataDir */ + Assert(t_thrd.proc_cxt.DataDir); + ValidatePgVersion(t_thrd.proc_cxt.DataDir); + + /* Change into t_thrd.proc_cxt.DataDir (if under postmaster, should be done already) */ + if (!IsUnderPostmaster) + ChangeToDataDir(); + + /* If standalone, create lockfile for data directory */ + if (!IsUnderPostmaster) + CreateDataDirLockFile(false); + + SetProcessingMode(BootstrapProcessing); + u_sess->attr.attr_common.IgnoreSystemIndexes = true; + + BaseInit(); + + pgstat_initialize(); + pgstat_bestart(); + if (!IsUnderPostmaster) { + ShareStorageInit(); + } + /* + * XLOG operations + */ + SetProcessingMode(NormalProcessing); + + switch (t_thrd.bootstrap_cxt.MyAuxProcType) { + case CheckerProcess: + /* don't set signals, they're useless here */ + CheckerModeMain(); + proc_exit(1); /* should never return */ + + case BootstrapProcess: + bootstrap_signals(); + BootStrapXLOG(); + MemoryContextUnSeal(t_thrd.top_mem_cxt); + BootstrapModeMain(); + MemoryContextSeal(t_thrd.top_mem_cxt); + proc_exit(1); /* should never return */ + + default: + ereport(PANIC, (errmsg("unrecognized process type: %d", (int)t_thrd.bootstrap_cxt.MyAuxProcType))); + proc_exit(1); + } +} + +/* + * In shared memory checker mode, all we really want to do is create shared + * memory and semaphores (just to prove we can do it with the current GUC + * settings). Since, in fact, that was already done by BaseInit(), + * we have nothing more to do here. + */ +static void CheckerModeMain(void) +{ + proc_exit(0); +} + +/* + * The main entry point for running the backend in bootstrap mode + * + * The bootstrap mode is used to initialize the template database. + * The bootstrap backend doesn't speak SQL, but instead expects + * commands in a special bootstrap language. + */ +static void BootstrapModeMain(void)//BootstrapModeMain函数用于完成系统引导模式,即系统启动阶段执行的函数 +{ + int i; + + Assert(!IsUnderPostmaster);// 断言,确认不是在后台进程中执行 + + SetProcessingMode(BootstrapProcessing);// 设置处理模式为引导模式 + + /* + * Do backend-like initialization for bootstrap mode + */ + InitProcess();//为引导模式做类似后台进程的初始化 + // 设置参数PostInit字段为NULL + t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(NULL, InvalidOid, NULL); + // 初始化引导模式 + t_thrd.proc_cxt.PostInit->InitBootstrap(); + + /* Initialize stuff for bootstrap-file processing */ + /* 初始化bootstrap文件处理的相关内容 */ + for (i = 0; i < MAXATTR; i++) { + t_thrd.bootstrap_cxt.attrtypes[i] = NULL;// 每个属性的类型初始化为NULL + Nulls[i] = false; // 每个属性的是否为空初始化为false + } + + /* + * Process bootstrap input. + */ + boot_yyparse();//处理bootstrap输入 + + /* + * We should now know about all mapped relations, so it's okay to write + * out the initial relation mapping files. + */ + RelationMapFinishBootstrap();// 调用boot_yyparse函数进行解析 + + /* Clean up and exit */ + cleanup();// 调用cleanup函数进行清理操作 + proc_exit(0);// 调用proc_exit函数结束进程 +} +/* ---------------------------------------------------------------- + * misc functions + * ---------------------------------------------------------------- + */ +/* + * Set up signal handling for a bootstrap process + */ + /* + 这个函数是一个初始化信号处理器的函数。根据是否在主服务器进程中运行来设置信号处理方式。 + +如果在主服务器进程中运行(IsUnderPostmaster为真),则设置一些信号的处理方式为忽略或默认处理。具体地: +- SIGHUP信号被设置为忽略 +- SIGINT信号(取消查询)被设置为忽略 +- SIGTERM信号被设置为调用die()函数 +- SIGQUIT信号被设置为调用quickdie()函数 +- SIGALRM、SIGPIPE、SIGUSR1和SIGUSR2信号被设置为忽略 +- SIGCHLD、SIGTTIN、SIGTTOU、SIGCONT和SIGWINCH信号被设置为默认处理 +- 解除阻塞的信号被解除阻塞 + +如果不在主服务器进程中运行,则设置一些信号的处理方式为调用die()函数,同时解除阻塞的SIGUSR2信号。 + +总之,该函数的作用是为了根据运行环境设置合适的信号处理方式。 + */ +static void bootstrap_signals(void) +{ + if (IsUnderPostmaster) { + /* + * Properly accept or ignore signals the postmaster might send us + */ + (void)gspqsignal(SIGHUP, SIG_IGN); + (void)gspqsignal(SIGINT, SIG_IGN); /* ignore query-cancel */ + (void)gspqsignal(SIGTERM, die); + (void)gspqsignal(SIGQUIT, quickdie); + (void)gspqsignal(SIGALRM, SIG_IGN); + (void)gspqsignal(SIGPIPE, SIG_IGN); + (void)gspqsignal(SIGUSR1, SIG_IGN); + (void)gspqsignal(SIGUSR2, SIG_IGN); + + /* + * Reset some signals that are accepted by postmaster but not here + */ + (void)gspqsignal(SIGCHLD, SIG_DFL); + (void)gspqsignal(SIGTTIN, SIG_DFL); + (void)gspqsignal(SIGTTOU, SIG_DFL); + (void)gspqsignal(SIGCONT, SIG_DFL); + (void)gspqsignal(SIGWINCH, SIG_DFL); + + /* + * Unblock signals (they were blocked when the postmaster forked us) + */ + gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); + (void)gs_signal_unblock_sigusr2(); + + } else { + /* Set up appropriately for interactive use */ + (void)gspqsignal(SIGHUP, die); + (void)gspqsignal(SIGINT, die); + (void)gspqsignal(SIGTERM, die); + (void)gspqsignal(SIGQUIT, die); + (void)gs_signal_unblock_sigusr2(); + } +} + +/* ---------------------------------------------------------------- + * MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS + * ---------------------------------------------------------------- + */ +/* ---------------- + * boot_openrel + * ---------------- + */ + /* + 这个函数的作用是打开一个关系(relation)。函数的输入参数是一个指向关系名的字符串。 +首先,函数检查关系名的长度是否超过了NAMEDATALEN。如果超过了,就将字符串的最后一个字符设置为'\0',这样就能确保字符串的长度不会超过NAMEDATALEN。 +然后,函数检查全局变量t_thrd.bootstrap_cxt.Typ是否为NULL。如果是NULL,说明还没有加载pg_type数据,需要加载该数据。 +加载pg_type数据的过程是从pg_type表中获取所有的行,并将行的数量存储到变量i中。然后,根据行的数量动态分配空间,并将空间的指针赋值给变量app。 +然后,循环执行i次,每次分配一个typmap结构的空间,并将该空间的指针存储到指针数组app中。最后,将数组的最后一个元素设置为NULL。 +接下来,重新开始扫描pg_type表,将扫描的结果存储到tup中。 +然后,将当前typmap结构的am_oid成员设置为tup的OID属性值,将am_typ成员设置为tup的实际数据,并将指针app递增1。循环扫描表的每一行,直到扫描结束。 +最后,关闭pg_type表,并将全局变量t_thrd.bootstrap_cxt.boot_reldesc设置为新打开的关系。获取关系的属性数量,并为每个属性分配一个空间。 +将关系的属性数据复制到属性空间中,并输出一些调试信息。 +这个函数的作用是打开一个关系,并加载关系的属性信息。 +加载属性信息的过程中,需要先加载pg_type表中的数据,并将数据存储到全局变量t_thrd.bootstrap_cxt.Typ中。 +然后根据关系的名称,打开关系并获取属性数量,为每个属性分配空间,并复制属性数据到空间中。 + */ +void boot_openrel(char* relname) +{ + int i; + struct typmap** app; + Relation rel; + TableScanDesc scan; + HeapTuple tup; + errno_t rc; + + if (strlen(relname) >= NAMEDATALEN) + relname[NAMEDATALEN - 1] = '\0'; + + if (t_thrd.bootstrap_cxt.Typ == NULL) { + /* We can now load the pg_type data */ + rel = heap_open(TypeRelationId, NoLock); + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + i = 0; + + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) + ++i; + tableam_scan_end(scan); + app = t_thrd.bootstrap_cxt.Typ = ALLOC(struct typmap*, i + 1); + while (i-- > 0) + *app++ = ALLOC(struct typmap, 1); + *app = NULL; + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + app = t_thrd.bootstrap_cxt.Typ; + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) { + (*app)->am_oid = HeapTupleGetOid(tup); + rc = + memcpy_s((char*)&(*app)->am_typ, sizeof((*app)->am_typ), (char*)GETSTRUCT(tup), sizeof((*app)->am_typ)); + securec_check(rc, "\0", "\0"); + app++; + } + tableam_scan_end(scan); + heap_close(rel, NoLock); + } + + if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) + closerel(NULL); + + ereport(DEBUG4, (errmsg("open relation %s, attrsize %d", relname, (int)ATTRIBUTE_FIXED_PART_SIZE))); + + t_thrd.bootstrap_cxt.boot_reldesc = heap_openrv(makeRangeVar(NULL, relname, -1), NoLock); + t_thrd.bootstrap_cxt.numattr = RelationGetNumberOfAttributes(t_thrd.bootstrap_cxt.boot_reldesc); + for (i = 0; i < t_thrd.bootstrap_cxt.numattr; i++) { + if (t_thrd.bootstrap_cxt.attrtypes[i] == NULL) + t_thrd.bootstrap_cxt.attrtypes[i] = AllocateAttribute(); + rc = memmove_s((char*)t_thrd.bootstrap_cxt.attrtypes[i], + ATTRIBUTE_FIXED_PART_SIZE, + (char*)t_thrd.bootstrap_cxt.boot_reldesc->rd_att->attrs[i], + ATTRIBUTE_FIXED_PART_SIZE); + securec_check(rc, "\0", "\0"); + + { + Form_pg_attribute at = t_thrd.bootstrap_cxt.attrtypes[i]; + + ereport(DEBUG4, + (errmsg("create attribute %d name %s len %d num %d type %u", + i, + NameStr(at->attname), + at->attlen, + at->attnum, + at->atttypid))); + } + } +} + +/* ---------------- + * closerel + * ---------------- + */ +给函数closerel添加注释: + +/** + * @brief 关闭指定的关系。 + * + * @param name 要关闭的关系的名称。 + */ +void closerel(char* name) +{ + // 检查是否传入了正确的参数 + if (name != NULL) { + // 检查是否存在已打开的关系 + if (t_thrd.bootstrap_cxt.boot_reldesc) { + // 检查要关闭的关系名是否与当前已打开的关系名不同 + if (strcmp(RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc), name) != 0) + // 如果不同,报错,提示预期的关系名和实际的关系名 + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("close of %s when %s was expected", + name, + RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc)))); + } else { + // 如果不存在已打开的关系,报错,提示关闭关系前未打开任何关系 + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("close of %s before any relation was opened", name))); + } + } + + // 检查是否存在已打开的关系 + if (t_thrd.bootstrap_cxt.boot_reldesc == NULL) + // 如果不存在已打开的关系,报错,提示没有可关闭的关系 + ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("no open relation to close"))); + else { + // 输出调试信息,关闭关系,将已打开的关系指针设置为NULL + ereport(DEBUG4, (errmsg("close relation %s", RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc)))); + heap_close(t_thrd.bootstrap_cxt.boot_reldesc, NoLock); + t_thrd.bootstrap_cxt.boot_reldesc = NULL; + } +} + +/* +* fix roluseft ,rolmonitoradmin, roloperatoradmin and rolpolicyadmin column of pg_authid to notnull +*/ +static void fix_attr_notnull(const char* name, int attnum) +{ + if (strncmp(name, "roluseft", strlen("roluseft")) == 0 && strlen(name) == strlen("roluseft")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + if (strncmp(name, "rolmonitoradmin", strlen("rolmonitoradmin")) == 0 && strlen(name) == strlen("rolmonitoradmin")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + if (strncmp(name, "roloperatoradmin", strlen("roloperatoradmin")) == 0 && + strlen(name) == strlen("roloperatoradmin")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + if (strncmp(name, "rolpolicyadmin", strlen("rolpolicyadmin")) == 0 && strlen(name) == strlen("rolpolicyadmin")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } +} + +/* ---------------- + * DEFINEATTR() + * + * define a pair + * if there are n fields in a relation to be created, this routine + * will be called n times + * ---------------- + */ + /* + 这个函数是用来定义数据库表中的一个属性(列)。函数的参数包括属性的名称(name)、数据类型(type)、以及属性的位置(attnum)。 +函数首先检查是否存在正在处理的关系(表),如果有,则发出警告并关闭该关系。 +接下来,函数分配一个新的Attribute结构体给t_thrd.bootstrap_cxt.attrtypes[attnum],并用0填充这个结构体。 +然后,函数将给定的属性名称和类型复制到Attribute结构体中,并设置了其他关于属性的一些信息,如编号(attnum + 1)、数据类型的OID值、数据类型的长度、是否将数据类型存储为基本类型等。 +接下来,函数会判断属性是否可以为空。如果属性是一个固定宽度的数据类型,或者前面的属性也是不可为空的变量(用C结构体声明访问),则将属性标记为"not null"。 +接着,函数检查特定的属性名称,并将这些属性标记为可为空或不可为空。 +这里列举了两个示例情况,第一个是将名为"partkey"、"intervaltablespace"、"intspnum"的属性标记为可为空,第二个是将名为"roluseft"、"rolmonitoradmin"、"roloperatoradmin"和"rolpolicyadmin"的属性标记为不可为空。 +总之,这个函数用于定义数据库表中的一个属性,包括属性的名称、数据类型和是否可为空等信息,并根据一些规则来判断是否将属性标记为"not null"。 + */ +void DefineAttr(const char* name, char* type, int attnum) +{ + Oid typeoid; + + if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) { + ereport(WARNING, (errmsg("no open relations allowed with CREATE command"))); + closerel(NULL); + } + + if (t_thrd.bootstrap_cxt.attrtypes[attnum] == NULL) + t_thrd.bootstrap_cxt.attrtypes[attnum] = AllocateAttribute(); + MemSet(t_thrd.bootstrap_cxt.attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE); + + (void)namestrcpy(&t_thrd.bootstrap_cxt.attrtypes[attnum]->attname, name); + ereport(DEBUG4, (errmsg("column %s %s", NameStr(t_thrd.bootstrap_cxt.attrtypes[attnum]->attname), type))); + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnum = attnum + 1; /* fillatt */ + + typeoid = gettype(type); + + if (t_thrd.bootstrap_cxt.Typ != NULL) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypid = t_thrd.bootstrap_cxt.Ap->am_oid; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen = t_thrd.bootstrap_cxt.Ap->am_typ.typlen; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attbyval = t_thrd.bootstrap_cxt.Ap->am_typ.typbyval; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attstorage = t_thrd.bootstrap_cxt.Ap->am_typ.typstorage; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attalign = t_thrd.bootstrap_cxt.Ap->am_typ.typalign; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attcollation = t_thrd.bootstrap_cxt.Ap->am_typ.typcollation; + /* if an array type, assume 1-dimensional attribute */ + if (t_thrd.bootstrap_cxt.Ap->am_typ.typelem != InvalidOid && t_thrd.bootstrap_cxt.Ap->am_typ.typlen < 0) + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 1; + else + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 0; + } else { + t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypid = TypInfo[typeoid].oid; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen = TypInfo[typeoid].len; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attbyval = TypInfo[typeoid].byval; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attstorage = TypInfo[typeoid].storage; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attalign = TypInfo[typeoid].align; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attcollation = TypInfo[typeoid].collation; + /* if an array type, assume 1-dimensional attribute */ + if (TypInfo[typeoid].elem != InvalidOid && t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen < 0) + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 1; + else + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 0; + } + + t_thrd.bootstrap_cxt.attrtypes[attnum]->attstattarget = -1; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attcacheoff = -1; + t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypmod = -1; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attislocal = true; + + /* + * Mark as "not null" if type is fixed-width and prior columns are too. + * This corresponds to case where column can be accessed directly via C + * struct declaration. + * + * oidvector and int2vector are also treated as not-nullable, even though + * they are no longer fixed-width. + */ +#define MARKNOTNULL(att) ((att)->attlen > 0 || (att)->atttypid == OIDVECTOROID || (att)->atttypid == INT2VECTOROID) + + if (MARKNOTNULL(t_thrd.bootstrap_cxt.attrtypes[attnum])) { + int i; + + for (i = 0; i < attnum; i++) { + if (!MARKNOTNULL(t_thrd.bootstrap_cxt.attrtypes[i])) + break; + } + if (i == attnum) + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + + // fix partkey/intervaltablespace/intspnum columns of pg_partition to nullable + if (strcmp(name, "partkey") == 0 || strcmp(name, "intervaltablespace") == 0 || strcmp(name, "intspnum") == 0) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = false; + } + + // fix roluseft ,rolmonitoradmin, roloperatoradmin and rolpolicyadmin column of pg_authid to notnull + fix_attr_notnull(name, attnum); + +} + +/* ---------------- + * InsertOneTuple + * + * If objectid is not zero, it is a specific OID to assign to the tuple. + * Otherwise, an OID will be assigned (if necessary) by heap_insert. + * ---------------- + */ + /*这个函数用于向表中插入一条元组。函数的参数是一个对象ID(objectid),表示要插入的元组的ID。函数通过`t_thrd.bootstrap_cxt.boot_reldesc`访问引导过程中的关系(表)描述符。 +首先,函数打印调试信息,包括要插入的行的ID和列数。然后,函数检查关系(表)描述符是否为引导过程中的pg_proc表的描述符,如果是,则报错,因为内置函数不应该被添加到pg_proc表中。 +接下来,函数调用`CreateTupleDesc`函数创建一个描述插入元组的元组描述符(tupDesc)。创建元组描述符时传递了一些参数,包括属性数目、关系是否具有物理上标识的Oid、属性类型数组以及关系的类型。 +然后,函数调用`tableam_tops_form_tuple`函数创建一个HeapTuple结构体,即要插入的元组。创建HeapTuple时使用了`tupDesc`、`values`(待插入的属性值数组)以及`Nulls`(表示每个属性是否为NULL的标记)。 +如果传递了非0的对象ID(objectid),则使用`HeapTupleSetOid`函数设置HeapTuple的对象ID。 +接下来,函数通过调用`simple_heap_insert`函数将HeapTuple插入到关系中。 +然后,函数通过调用`tableam_tops_free_tuple`函数释放之前创建的HeapTuple。 +最后,函数在插入完成后打印调试信息,并通过循环将`Nulls`数组重置为false,以便下一次插入元组时使用。 +总之,这个函数用于插入一条元组到表中。函数创建一个插入元组的元组描述符,然后根据传递的属性值和标记创建一个HeapTuple,并将其插入到关系中。最后,函数释放已创建的HeapTuple,并重置标记数组以备下次插入使用。*/ +void InsertOneTuple(Oid objectid) +{ + HeapTuple tuple; + TupleDesc tupDesc; + int i; + + ereport(DEBUG4, (errmsg("inserting row oid %u, %d columns", objectid, t_thrd.bootstrap_cxt.numattr))); + + if (IsBootingPgProc(t_thrd.bootstrap_cxt.boot_reldesc)) { + ereport(FATAL, (errmsg("Built-in functions should not be added into pg_proc"))); + } + tupDesc = CreateTupleDesc(t_thrd.bootstrap_cxt.numattr, + RelationGetForm(t_thrd.bootstrap_cxt.boot_reldesc)->relhasoids, + t_thrd.bootstrap_cxt.attrtypes, + t_thrd.bootstrap_cxt.boot_reldesc->rd_tam_type); + tuple = (HeapTuple) tableam_tops_form_tuple(tupDesc, values, Nulls, HEAP_TUPLE); + if (objectid != (Oid)0) + HeapTupleSetOid(tuple, objectid); + pfree(tupDesc); /* just free's tupDesc, not the attrtypes */ + + (void)simple_heap_insert(t_thrd.bootstrap_cxt.boot_reldesc, tuple); + tableam_tops_free_tuple(tuple); + ereport(DEBUG4, (errmsg("row inserted"))); + + /* + * Reset null markers for next tuple + */ + for (i = 0; i < t_thrd.bootstrap_cxt.numattr; i++) + Nulls[i] = false; +} + +/* ---------------- + * InsertOneValue + * ---------------- + */ +void InsertOneValue(char* value, int i) +{ + Oid typoid; + int16 typlen; + bool typbyval = false; + char typalign; + char typdelim; + Oid typioparam; + Oid typinput; + Oid typoutput; + char* prt = NULL; + + AssertArg(i >= 0 && i < MAXATTR); + + ereport(DEBUG4, (errmsg("inserting column %d value \"%s\"", i, value))); + + typoid = t_thrd.bootstrap_cxt.boot_reldesc->rd_att->attrs[i]->atttypid; + + boot_get_type_io_data(typoid, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typinput, &typoutput); + + values[i] = OidInputFunctionCall(typinput, value, typioparam, -1); + prt = OidOutputFunctionCall(typoutput, values[i]); + ereport(DEBUG4, (errmsg("inserted -> %s", prt))); + pfree(prt); +} + +/* ---------------- + * InsertOneNull + * ---------------- + */ +void InsertOneNull(int i) +{ + ereport(DEBUG4, (errmsg("inserting column %d NULL", i))); + Assert(i >= 0 && i < MAXATTR); + values[i] = PointerGetDatum(NULL); + Nulls[i] = true; +} + +/* ---------------- + * cleanup + * ---------------- + */ +static void cleanup(void) +{ + if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) + closerel(NULL); +} + +/* ---------------- + * gettype + * + * NB: this is really ugly; it will return an integer index into TypInfo[], + * and not an OID at all, until the first reference to a type not known in + * TypInfo[]. At that point it will read and cache pg_type in the Typ array, + * and subsequently return a real OID (and set the global pointer Ap to + * point at the found row in Typ). So caller must check whether Typ is + * still NULL to determine what the return value is! + * ---------------- + */ +static Oid gettype(char* type) +{ + int i; + Relation rel; + TableScanDesc scan; + HeapTuple tup; + struct typmap** app; + errno_t rc; + + if (t_thrd.bootstrap_cxt.Typ != NULL) { + for (app = t_thrd.bootstrap_cxt.Typ; *app != NULL; app++) { + if (strncmp(NameStr((*app)->am_typ.typname), type, NAMEDATALEN) == 0) { + t_thrd.bootstrap_cxt.Ap = *app; + return (*app)->am_oid; + } + } + } else { + for (i = 0; i < n_types; i++) { + if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0) + return i; + } + ereport(DEBUG4, (errmsg("external type: %s", type))); + rel = heap_open(TypeRelationId, NoLock); + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + i = 0; + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) + ++i; + tableam_scan_end(scan); + app = t_thrd.bootstrap_cxt.Typ = ALLOC(struct typmap*, i + 1); + while (i-- > 0) + *app++ = ALLOC(struct typmap, 1); + *app = NULL; + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + app = t_thrd.bootstrap_cxt.Typ; + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) { + (*app)->am_oid = HeapTupleGetOid(tup); + rc = memmove_s( + (char*)&(*app++)->am_typ, sizeof((*app)->am_typ), (char*)GETSTRUCT(tup), sizeof((*app)->am_typ)); + securec_check(rc, "\0", "\0"); + } + tableam_scan_end(scan); + heap_close(rel, NoLock); + return gettype(type); + } + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unrecognized type \"%s\"", type))); + /* not reached, here to make compiler happy */ + return 0; +} + +/* ---------------- + * boot_get_type_io_data + * + * Obtain type I/O information at bootstrap time. This intentionally has + * almost the same API as lsyscache.c's get_type_io_data, except that + * we only support obtaining the typinput and typoutput routines, not + * the binary I/O routines. It is exported so that array_in and array_out + * can be made to work during early bootstrap. + * ---------------- + */ + /* + 这个函数用于获取指定类型(typid)的输入/输出数据。函数通过指定的typid查找对应的类型信息,并将这些信息通过输出参数返回给调用者。 +首先,函数检查`t_thrd.bootstrap_cxt.Typ`是否为空。如果不为空,说明在引导过程中已经获取到了`pg_type`表的内容。接下来,函数在`t_thrd.bootstrap_cxt.Typ`中查找指定的typid对应的类型信息,并将找到的信息保存在`ap`结构体中。 +如果没有找到指定的typid对应的信息,则报错,提示类型OID未在Typ列表中找到。 +接着,函数通过将`ap`结构体中的成员值赋给输出参数,将类型信息返回给调用者。具体包括类型的长度(typlen)、是否传递值(typbyval)、对齐方式(typalign)、分隔符(typdelim)、类型输入函数参数(typioparam)、类型输入函数(typinput)、类型输出函数(typoutput)。 +如果`t_thrd.bootstrap_cxt.Typ`为空,说明还没有获取到`pg_type`表的内容。在这种情况下,函数将使用固定的`TypInfo`数组来获取类型信息。函数通过遍历`TypInfo`数组,查找指定typid对应的类型信息,并将找到的信息保存在`typeindex`变量中。 +如果找不到指定typid对应的类型信息,则报错,提示类型OID在TypInfo中未找到。 +接下来,函数通过将`TypInfo`数组中的成员值赋给输出参数,将类型信息返回给调用者。具体包括类型的长度(typlen)、是否传递值(typbyval)、对齐方式(typalign)、分隔符(typdelim)、类型输入函数参数(typioparam)、类型输入函数(typinput)、类型输出函数(typoutput)。 +总之,这个函数用于获取指定类型的输入/输出数据。函数根据是否已经获取到`pg_type`表的内容来决定是使用`pg_type`中的类型信息,还是使用固定的`TypInfo`数组中的类型信息。然后,函数将获取到的类型信息通过输出参数返回给调用者。 + */ +void boot_get_type_io_data(Oid typid, int16* typlen, bool* typbyval, char* typalign, char* typdelim, Oid* typioparam, + Oid* typinput, Oid* typoutput) +{ + if (t_thrd.bootstrap_cxt.Typ != NULL) { + /* We have the boot-time contents of pg_type, so use it */ + struct typmap** app; + struct typmap* ap = NULL; + + app = t_thrd.bootstrap_cxt.Typ; + while (*app && (*app)->am_oid != typid) + ++app; + ap = *app; + if (ap == NULL) + ereport( + ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("type OID %u not found in Typ list", typid))); + + *typlen = ap->am_typ.typlen; + *typbyval = ap->am_typ.typbyval; + *typalign = ap->am_typ.typalign; + *typdelim = ap->am_typ.typdelim; + + /* XXX this logic must match getTypeIOParam() */ + if (OidIsValid(ap->am_typ.typelem)) + *typioparam = ap->am_typ.typelem; + else + *typioparam = typid; + + *typinput = ap->am_typ.typinput; + *typoutput = ap->am_typ.typoutput; + } else { + /* We don't have pg_type yet, so use the hard-wired TypInfo array */ + int typeindex; + + for (typeindex = 0; typeindex < n_types; typeindex++) { + if (TypInfo[typeindex].oid == typid) + break; + } + if (typeindex >= n_types) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("type OID %u not found in TypInfo", typid))); + + *typlen = TypInfo[typeindex].len; + *typbyval = TypInfo[typeindex].byval; + *typalign = TypInfo[typeindex].align; + /* We assume typdelim is ',' for all boot-time types */ + *typdelim = ','; + + /* XXX this logic must match getTypeIOParam() */ + if (OidIsValid(TypInfo[typeindex].elem)) + *typioparam = TypInfo[typeindex].elem; + else + *typioparam = typid; + + *typinput = TypInfo[typeindex].inproc; + *typoutput = TypInfo[typeindex].outproc; + } +} + +/* ---------------- + * AllocateAttribute + * + * Note: bootstrap never sets any per-column ACLs, so we only need + * ATTRIBUTE_FIXED_PART_SIZE space per attribute. + * ---------------- + */ +static Form_pg_attribute AllocateAttribute(void) +{ + Form_pg_attribute attribute = (Form_pg_attribute)MemoryContextAlloc( + SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), ATTRIBUTE_FIXED_PART_SIZE); + + if (!PointerIsValid(attribute)) + ereport(FATAL, (errmsg("out of memory"))); + MemSet(attribute, 0, ATTRIBUTE_FIXED_PART_SIZE); + + return attribute; +} + +/* ---------------- + * MapArrayTypeName + * XXX arrays of "basetype" are always "_basetype". + * this is an evil hack inherited from rel. 3.1. + * XXX array dimension is thrown away because we + * don't support fixed-dimension arrays. again, + * sickness from 3.1. + * + * the string passed in must have a '[' character in it + * + * the string returned is a pointer to static storage and should NOT + * be freed by the CALLER. + * ---------------- + */ +const char* MapArrayTypeName(const char* s) +{ + int i; + int j; + + if (s == NULL || s[0] == '\0') + return s; + + j = 1; + t_thrd.bootstrap_cxt.newStr[0] = '_'; + for (i = 0; i < NAMEDATALEN - 1 && s[i] != '['; i++, j++) + t_thrd.bootstrap_cxt.newStr[j] = s[i]; + + t_thrd.bootstrap_cxt.newStr[j] = '\0'; + + return t_thrd.bootstrap_cxt.newStr; +} + +/* + * index_register() -- record an index that has been set up for building + * later. + * + * At bootstrap time, we define a bunch of indexes on system catalogs. + * We postpone actually building the indexes until just before we're + * finished with initialization, however. This is because the indexes + * themselves have catalog entries, and those have to be included in the + * indexes on those catalogs. Doing it in two phases is the simplest + * way of making sure the indexes have the right contents at the end. + */ + /*这个函数用于在引导过程中注册索引。函数接收三个参数:heap(堆表的对象ID)、ind(索引的对象ID)和indexInfo(IndexInfo结构体的指针,包含了索引的详细信息)。 +函数。首先,函数创建一个IndexList结构体的实例newind,并将其初始化为NULL。 +接下来,函数检查是否已经创建了t_thrd.bootstrap_cxt.nogc上下文,如果没有,则创建一个名为"BootstrapNoGC"的上下文。这个上下文用于在引导过程中暂时保存索引的相关信息,防止其被垃圾回收。 +然后函数将当前的内存上下文切换到t_thrd.bootstrap_cxt.nogc上下文。 +接着,函数分配一个IndexList结构体的内存,并将heap、ind和indexInfo的值分别赋给新分配的结构体的相应成员变量。 +然后函数通过memcpy_s函数将indexInfo结构体的内容复制到newind->il_info中。同时,函数使用copyObject函数分别复制indexInfo->ii_Expressions和indexInfo->ii_Predicate,并将复制后的值分别赋给newind->il_info->ii_Expressions和newind->il_info->ii_Predicate。 +接下来,函数将newind添加到t_thrd.bootstrap_cxt.ILHead链表中,以便在稍后的操作中使用。 +最后,函数将内存上下文切换回先前的上下文。 +总之,这个函数用于在引导过程中注册索引。它创建一个表示索引的IndexList结构体对象,并将索引相关的信息保存在其中。然后,它将这个对象添加到上下文链表中以备后续使用。*/ +void index_register(Oid heap, Oid ind, IndexInfo* indexInfo) +{ + IndexList* newind = NULL; + MemoryContext oldcxt; + errno_t rc; + + /* + * XXX mao 10/31/92 -- don't gc index reldescs, associated info at + * bootstrap time. we'll declare the indexes now, but want to create them + * later. + */ + if (t_thrd.bootstrap_cxt.nogc == NULL) + t_thrd.bootstrap_cxt.nogc = AllocSetContextCreate( + NULL, "BootstrapNoGC", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); + + oldcxt = MemoryContextSwitchTo(t_thrd.bootstrap_cxt.nogc); + + newind = (IndexList*)palloc(sizeof(IndexList)); + newind->il_heap = heap; + newind->il_ind = ind; + newind->il_info = (IndexInfo*)palloc(sizeof(IndexInfo)); + + rc = memcpy_s(newind->il_info, sizeof(IndexInfo), indexInfo, sizeof(IndexInfo)); + securec_check(rc, "\0", "\0"); + /* expressions will likely be null, but may as well copy it */ + newind->il_info->ii_Expressions = (List*)copyObject(indexInfo->ii_Expressions); + newind->il_info->ii_ExpressionsState = NIL; + /* predicate will likely be null, but may as well copy it */ + newind->il_info->ii_Predicate = (List*)copyObject(indexInfo->ii_Predicate); + newind->il_info->ii_PredicateState = NIL; + /* no exclusion constraints at bootstrap time, so no need to copy */ + Assert(indexInfo->ii_ExclusionOps == NULL); + Assert(indexInfo->ii_ExclusionProcs == NULL); + Assert(indexInfo->ii_ExclusionStrats == NULL); + + newind->il_next = t_thrd.bootstrap_cxt.ILHead; + t_thrd.bootstrap_cxt.ILHead = newind; + + (void)MemoryContextSwitchTo(oldcxt); +} + +/* + * build_indices -- fill in all the indexes registered earlier + */ + /* + 这个函数用于在引导过程中构建索引。它通过遍历t_thrd.bootstrap_cxt.ILHead链表中的每个索引来逐个构建索引。 +循环的迭代条件是t_thrd.bootstrap_cxt.ILHead不为空,也就是说还有待构建的索引。循环的每次迭代,我们会定义两个Relation对象:heap和ind,分别用于表示堆表和索引表。 +引导过程中不需要考虑获取锁的问题,所以我们使用heap_open和index_open函数打开堆表和索引表。这两个函数接收两个参数:表的对象ID和锁的模式(NoLock表示不获取锁)。返回的Relation对象分别赋给heap和ind变量。 +然后,我们调用index_build函数来构建索引。这个函数接收多个参数,包括堆表、分区信息、索引表、并行标志、索引详细信息等。这些参数的值分别来自t_thrd.bootstrap_cxt.ILHead链表的当前节点。函数会使用这些参数来构建索引。 +索引构建完成后,我们使用index_close和heap_close函数关闭索引表和堆表。这些函数同样需要传入锁的模式参数(NoLock)。 +总之,这个函数用于在引导过程中构建索引。通过遍历t_thrd.bootstrap_cxt.ILHead链表中的每个索引,我们依次打开堆表和索引表,并调用index_build函数进行索引构建。最后,我们关闭索引表和堆表。 + */ +void build_indices(void) +{ + for (; t_thrd.bootstrap_cxt.ILHead != NULL; t_thrd.bootstrap_cxt.ILHead = t_thrd.bootstrap_cxt.ILHead->il_next) { + Relation heap; + Relation ind; + + /* need not bother with locks during bootstrap */ + heap = heap_open(t_thrd.bootstrap_cxt.ILHead->il_heap, NoLock); + ind = index_open(t_thrd.bootstrap_cxt.ILHead->il_ind, NoLock); + index_build( + heap, NULL, ind, NULL, t_thrd.bootstrap_cxt.ILHead->il_info, false, false, INDEX_CREATE_NONE_PARTITION); + + index_close(ind, NoLock); + heap_close(heap, NoLock); + } +} -- 2.34.1 From 70b4e5154f8a673cc7bbaefd41c536e367ba8f0a Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:13:30 +0800 Subject: [PATCH 17/56] ADD file via upload --- src/gausskernel/bootstrap/bootstrap.cpp | 1137 +++++++++++++++++++++++ 1 file changed, 1137 insertions(+) create mode 100644 src/gausskernel/bootstrap/bootstrap.cpp diff --git a/src/gausskernel/bootstrap/bootstrap.cpp b/src/gausskernel/bootstrap/bootstrap.cpp new file mode 100644 index 000000000..f35dfe39d --- /dev/null +++ b/src/gausskernel/bootstrap/bootstrap.cpp @@ -0,0 +1,1137 @@ +/* ------------------------------------------------------------------------- + * + * bootstrap.c + * routines to support running openGauss in 'bootstrap' mode + * bootstrap mode is used to create the initial template database + * + * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 2010-2012 Postgres-XC Development Group + * + * IDENTIFICATION + * src/backend/bootstrap/bootstrap.c + * + * ------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "knl/knl_variable.h" +#include "pgstat.h" +#include +#include +#include +#ifdef HAVE_GETOPT_H +#include +#endif + +#include "access/tableam.h" +#include "bootstrap/bootstrap.h" +#include "catalog/index.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "libpq/pqsignal.h" +#include "miscadmin.h" +#include "pgstat.h" +#include "nodes/makefuncs.h" +#include "postmaster/aiocompleter.h" +#include "postmaster/bgwriter.h" +#include "postmaster/pagewriter.h" +#include "postmaster/cbmwriter.h" +#include "postmaster/startup.h" +#include "postmaster/twophasecleaner.h" +#include "postmaster/licensechecker.h" +#include "postmaster/walwriter.h" +#include "postmaster/lwlockmonitor.h" +#include "replication/walreceiver.h" +#include "replication/datareceiver.h" +#include "storage/buf/bufmgr.h" +#include "storage/ipc.h" +#include "storage/proc.h" +#include "tcop/tcopprot.h" +#include "threadpool/threadpool.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/guc_storage.h" +#include "utils/memutils.h" +#include "utils/plog.h" +#include "utils/postinit.h" +#include "utils/ps_status.h" +#include "utils/rel.h" +#include "utils/rel_gs.h" +#include "utils/relmapper.h" +#include "utils/snapmgr.h" +#include "access/parallel_recovery/page_redo.h" + +#ifdef PGXC +#include "nodes/nodes.h" +#include "pgxc/poolmgr.h" +#endif + +#include "gssignal/gs_signal.h" + +#define ALLOC(t, c) ((t*)selfpalloc0((unsigned)(c) * sizeof(t))) + +static void CheckerModeMain(void); +static void BootstrapModeMain(void); +static void bootstrap_signals(void); +static Form_pg_attribute AllocateAttribute(void); +static Oid gettype(char* type); +static void cleanup(void); + +/* + * Basic information associated with each type. This is used before + * pg_type is filled, so it has to cover the datatypes used as column types + * in the core "bootstrapped" catalogs. + * + * XXX several of these input/output functions do catalog scans + * (e.g., F_REGPROCIN scans pg_proc). this obviously creates some + * order dependencies in the catalog creation process. + */ +struct typinfo { + char name[NAMEDATALEN]; + Oid oid; + Oid elem; + int16 len; + bool byval; + char align; + char storage; + Oid collation; + Oid inproc; + Oid outproc; +}; + +static const struct typinfo TypInfo[] = {{"bool", BOOLOID, 0, 1, true, 'c', 'p', InvalidOid, F_BOOLIN, F_BOOLOUT}, + {"bytea", BYTEAOID, 0, -1, false, 'i', 'x', InvalidOid, F_BYTEAIN, F_BYTEAOUT}, + {"char", CHAROID, 0, 1, true, 'c', 'p', InvalidOid, F_CHARIN, F_CHAROUT}, + {"int1", INT1OID, 0, 1, true, 'c', 'p', InvalidOid, F_INT1IN, F_INT1OUT}, + {"int2", INT2OID, 0, 2, true, 's', 'p', InvalidOid, F_INT2IN, F_INT2OUT}, + {"int4", INT4OID, 0, 4, true, 'i', 'p', InvalidOid, F_INT4IN, F_INT4OUT}, + {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid, F_FLOAT4IN, F_FLOAT4OUT}, + {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', InvalidOid, F_NAMEIN, F_NAMEOUT}, + {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGCLASSIN, F_REGCLASSOUT}, + {"regproc", REGPROCOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGPROCIN, F_REGPROCOUT}, + {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGTYPEIN, F_REGTYPEOUT}, + {"text", TEXTOID, 0, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_TEXTIN, F_TEXTOUT}, + {"oid", OIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_OIDIN, F_OIDOUT}, + {"tid", TIDOID, 0, 6, false, 's', 'p', InvalidOid, F_TIDIN, F_TIDOUT}, + {"xid", XIDOID, 0, 8, FLOAT8PASSBYVAL, 'd', 'p', InvalidOid, F_XIDIN, F_XIDOUT}, + {"xid32", SHORTXIDOID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid, F_XIDIN4, F_XIDOUT4}, + {"cid", CIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_CIDIN, F_CIDOUT}, + {"pg_node_tree", + PGNODETREEOID, + 0, + -1, + false, + 'i', + 'x', + DEFAULT_COLLATION_OID, + F_PG_NODE_TREE_IN, + F_PG_NODE_TREE_OUT}, + {"int2vector", INT2VECTOROID, INT2OID, -1, false, 'i', 'p', InvalidOid, F_INT2VECTORIN, F_INT2VECTOROUT}, + {"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p', InvalidOid, F_OIDVECTORIN, F_OIDVECTOROUT}, + {"_int2", INT2ARRAYOID, INT2OID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_text", 1009, TEXTOID, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_ARRAY_IN, F_ARRAY_OUT}, + {"_oid", 1028, OIDOID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_char", 1002, CHAROID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, + {"raw", RAWOID, 0, -1, false, 'i', 'x', InvalidOid, F_BYTEAIN, F_BYTEAOUT}, + {"oidvector_extend", + OIDVECTOREXTENDOID, + OIDOID, + -1, + false, + 'i', + 'x', + InvalidOid, + F_OIDVECTORIN_EXTEND, + F_OIDVECTOROUT_EXTEND}, + {"int2vector_extend", + INT2VECTOREXTENDOID, + INT2OID, + -1, + false, + 'i', + 'x', + InvalidOid, + F_INT2VECTORIN, + F_INT2VECTOROUT}}; + +static const int n_types = sizeof(TypInfo) / sizeof(struct typinfo); + +struct typmap { /* a hack */ + Oid am_oid; + FormData_pg_type am_typ; +}; + +static THR_LOCAL Datum values[MAXATTR]; /* current row's attribute values */ +static THR_LOCAL bool Nulls[MAXATTR]; + +/* + * At bootstrap time, we first declare all the indices to be built, and + * then build them. The IndexList structure stores enough information + * to allow us to build the indices after they've been declared. + */ +typedef struct _IndexList { + Oid il_heap; + Oid il_ind; + IndexInfo* il_info; + struct _IndexList* il_next; +} IndexList; + +/* + * BootStrapProcessMain + * + * The main entry point for auxiliary processes, such as the bgwriter, + * walwriter, walreceiver, bootstrapper and the shared memory checker code. + * + * This code is here just because of historical reasons. + */ +//这是一个主要的引导启动进程。它包含以下主要步骤: +//初始化全局变量,设置进程ID(PostmasterPid)和启动时间(MyStartTime)。 +//使用进程ID和启动时间作为种子初始化随机数。 +//初始化错误和内存管理子系统。 +//初始化全局配置选项。 +//处理命令行参数。根据参数设置相应的配置选项。 +//验证并设置数据目录。 +//创建数据目录的锁文件。 +//设置处理模式为BootstrapProcessing。 +//初始化基本的后台进程。 +//初始化统计信息收集。 +//根据进程类型执行相应的操作。 +//如果进程类型是CheckerProcess,则执行CheckerModeMain()函数,并退出进程。 +//如果进程类型是BootstrapProcess,则设置信号处理函数,执行BootStrapXLOG()函数,然后执行BootstrapModeMain()函数,并退出进程。 +//如果进程类型未被识别,则触发PANIC错误,并退出进程。 +void BootStrapProcessMain(int argc, char* argv[]) +{ + char* progName = argv[0]; + int flag; + char* userDoption = NULL; + OptParseContext optCtxt; + errno_t errorno = EOK; + + /* + * initialize globals + */ + PostmasterPid = gs_thread_self(); + + t_thrd.proc_cxt.MyProcPid = gs_thread_self(); + + t_thrd.proc_cxt.MyStartTime = time(NULL); + + /* + * Initialize random() for the first time, like PostmasterMain() would. + * In a regular IsUnderPostmaster backend, BackendRun() computes a + * high-entropy seed before any user query. Fewer distinct initial seeds + * can occur here. + */ + srandom((unsigned int)(t_thrd.proc_cxt.MyProcPid ^ (unsigned int)t_thrd.proc_cxt.MyStartTime)); + + t_thrd.proc_cxt.MyProgName = "BootStrap"; + /* + * Fire up essential subsystems: error and memory management + * + * If we are running under the postmaster, this is done already. + */ + if (!IsUnderPostmaster) { + MemoryContextInit(); + init_plog_global_mem(); + } + + /* Compute paths, if we didn't inherit them from postmaster */ + if (my_exec_path[0] == '\0') { + if (find_my_exec(progName, my_exec_path) < 0) + ereport(FATAL, (errmsg("%s: could not locate my own executable path", progName))); + } + + /* + * process command arguments + */ + /* Set defaults, to be overriden by explicit options below */ + if (!IsUnderPostmaster) { + InitializeGUCOptions(); + } + + /* Ignore the initial --boot argument, if present */ + if (argc > 1 && strcmp(argv[1], "--boot") == 0) { + argv++; + argc--; + } + + /* If no -x argument, we are a CheckerProcess */ + t_thrd.bootstrap_cxt.MyAuxProcType = CheckerProcess; + + initOptParseContext(&optCtxt); + while ((flag = getopt_r(argc, argv, "B:c:d:D:Fr:x:g:-:", &optCtxt)) != -1) { + switch (flag) { + case 'B': + SetConfigOption("shared_buffers", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV); + break; + case 'D': + userDoption = optCtxt.optarg; + break; + case 'd': { + int debugStrLen = strlen("debug") + strlen(optCtxt.optarg) + 1; + /* Turn on debugging for the bootstrap process. */ + char* debugstr = (char*)palloc(debugStrLen); + + errorno = snprintf_s(debugstr, debugStrLen, debugStrLen - 1, "debug%s", optCtxt.optarg); + securec_check_ss(errorno, "\0", "\0"); + SetConfigOption("log_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV); + SetConfigOption("client_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV); + pfree(debugstr); + } break; + case 'F': + SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV); + break; + case 'g': + SetConfigOption("xlog_file_path", optCtxt.optarg, PGC_POSTMASTER, PGC_S_ARGV); + break; + case 'r': + errorno = strcpy_s(t_thrd.proc_cxt.OutputFileName, MAXPGPATH, optCtxt.optarg); + securec_check(errorno, "\0", "\0"); + break; + case 'x': + t_thrd.bootstrap_cxt.MyAuxProcType = (AuxProcType)atoi(optCtxt.optarg); + break; + case 'c': + case '-': { + char* name = NULL; + char* value = NULL; + + ParseLongOption(optCtxt.optarg, &name, &value); + if (value == NULL) { + if (flag == '-') + ereport( + ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("--%s requires a value", optCtxt.optarg))); + else + ereport( + ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("-c %s requires a value", optCtxt.optarg))); + } + + SetConfigOption(name, value, PGC_POSTMASTER, PGC_S_ARGV); + pfree(name); + if (value != NULL) + pfree(value); + break; + } + default: + write_stderr("Try \"%s --help\" for more information.\n", progName); + proc_exit(1); + break; + } + } + + if (argc != optCtxt.optind) { + write_stderr("%s: invalid command-line arguments\n", progName); + proc_exit(1); + } + + /* Acquire configuration parameters, unless inherited from postmaster */ + if (!IsUnderPostmaster) { + if (!SelectConfigFiles(userDoption, progName)) { + proc_exit(1); + } + InitializeNumLwLockPartitions(); + } + g_instance.global_sysdbcache.Init(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); + CreateLocalSysDBCache(); + + /* Validate we have been given a reasonable-looking t_thrd.proc_cxt.DataDir */ + Assert(t_thrd.proc_cxt.DataDir); + ValidatePgVersion(t_thrd.proc_cxt.DataDir); + + /* Change into t_thrd.proc_cxt.DataDir (if under postmaster, should be done already) */ + if (!IsUnderPostmaster) + ChangeToDataDir(); + + /* If standalone, create lockfile for data directory */ + if (!IsUnderPostmaster) + CreateDataDirLockFile(false); + + SetProcessingMode(BootstrapProcessing); + u_sess->attr.attr_common.IgnoreSystemIndexes = true; + + BaseInit(); + + pgstat_initialize(); + pgstat_bestart(); + if (!IsUnderPostmaster) { + ShareStorageInit(); + } + /* + * XLOG operations + */ + SetProcessingMode(NormalProcessing); + + switch (t_thrd.bootstrap_cxt.MyAuxProcType) { + case CheckerProcess: + /* don't set signals, they're useless here */ + CheckerModeMain(); + proc_exit(1); /* should never return */ + + case BootstrapProcess: + bootstrap_signals(); + BootStrapXLOG(); + MemoryContextUnSeal(t_thrd.top_mem_cxt); + BootstrapModeMain(); + MemoryContextSeal(t_thrd.top_mem_cxt); + proc_exit(1); /* should never return */ + + default: + ereport(PANIC, (errmsg("unrecognized process type: %d", (int)t_thrd.bootstrap_cxt.MyAuxProcType))); + proc_exit(1); + } +} + +/* + * In shared memory checker mode, all we really want to do is create shared + * memory and semaphores (just to prove we can do it with the current GUC + * settings). Since, in fact, that was already done by BaseInit(), + * we have nothing more to do here. + */ +static void CheckerModeMain(void) +{ + proc_exit(0); +} + +/* + * The main entry point for running the backend in bootstrap mode + * + * The bootstrap mode is used to initialize the template database. + * The bootstrap backend doesn't speak SQL, but instead expects + * commands in a special bootstrap language. + */ +static void BootstrapModeMain(void)//BootstrapModeMain函数用于完成系统引导模式,即系统启动阶段执行的函数 +{ + int i; + + Assert(!IsUnderPostmaster);// 断言,确认不是在后台进程中执行 + + SetProcessingMode(BootstrapProcessing);// 设置处理模式为引导模式 + + /* + * Do backend-like initialization for bootstrap mode + */ + InitProcess();//为引导模式做类似后台进程的初始化 + // 设置参数PostInit字段为NULL + t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(NULL, InvalidOid, NULL); + // 初始化引导模式 + t_thrd.proc_cxt.PostInit->InitBootstrap(); + + /* Initialize stuff for bootstrap-file processing */ + /* 初始化bootstrap文件处理的相关内容 */ + for (i = 0; i < MAXATTR; i++) { + t_thrd.bootstrap_cxt.attrtypes[i] = NULL;// 每个属性的类型初始化为NULL + Nulls[i] = false; // 每个属性的是否为空初始化为false + } + + /* + * Process bootstrap input. + */ + boot_yyparse();//处理bootstrap输入 + + /* + * We should now know about all mapped relations, so it's okay to write + * out the initial relation mapping files. + */ + RelationMapFinishBootstrap();// 调用boot_yyparse函数进行解析 + + /* Clean up and exit */ + cleanup();// 调用cleanup函数进行清理操作 + proc_exit(0);// 调用proc_exit函数结束进程 +} +/* ---------------------------------------------------------------- + * misc functions + * ---------------------------------------------------------------- + */ +/* + * Set up signal handling for a bootstrap process + */ + /* + 这个函数是一个初始化信号处理器的函数。根据是否在主服务器进程中运行来设置信号处理方式。 + +如果在主服务器进程中运行(IsUnderPostmaster为真),则设置一些信号的处理方式为忽略或默认处理。具体地: +- SIGHUP信号被设置为忽略 +- SIGINT信号(取消查询)被设置为忽略 +- SIGTERM信号被设置为调用die()函数 +- SIGQUIT信号被设置为调用quickdie()函数 +- SIGALRM、SIGPIPE、SIGUSR1和SIGUSR2信号被设置为忽略 +- SIGCHLD、SIGTTIN、SIGTTOU、SIGCONT和SIGWINCH信号被设置为默认处理 +- 解除阻塞的信号被解除阻塞 + +如果不在主服务器进程中运行,则设置一些信号的处理方式为调用die()函数,同时解除阻塞的SIGUSR2信号。 + +总之,该函数的作用是为了根据运行环境设置合适的信号处理方式。 + */ +static void bootstrap_signals(void) +{ + if (IsUnderPostmaster) { + /* + * Properly accept or ignore signals the postmaster might send us + */ + (void)gspqsignal(SIGHUP, SIG_IGN); + (void)gspqsignal(SIGINT, SIG_IGN); /* ignore query-cancel */ + (void)gspqsignal(SIGTERM, die); + (void)gspqsignal(SIGQUIT, quickdie); + (void)gspqsignal(SIGALRM, SIG_IGN); + (void)gspqsignal(SIGPIPE, SIG_IGN); + (void)gspqsignal(SIGUSR1, SIG_IGN); + (void)gspqsignal(SIGUSR2, SIG_IGN); + + /* + * Reset some signals that are accepted by postmaster but not here + */ + (void)gspqsignal(SIGCHLD, SIG_DFL); + (void)gspqsignal(SIGTTIN, SIG_DFL); + (void)gspqsignal(SIGTTOU, SIG_DFL); + (void)gspqsignal(SIGCONT, SIG_DFL); + (void)gspqsignal(SIGWINCH, SIG_DFL); + + /* + * Unblock signals (they were blocked when the postmaster forked us) + */ + gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); + (void)gs_signal_unblock_sigusr2(); + + } else { + /* Set up appropriately for interactive use */ + (void)gspqsignal(SIGHUP, die); + (void)gspqsignal(SIGINT, die); + (void)gspqsignal(SIGTERM, die); + (void)gspqsignal(SIGQUIT, die); + (void)gs_signal_unblock_sigusr2(); + } +} + +/* ---------------------------------------------------------------- + * MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS + * ---------------------------------------------------------------- + */ +/* ---------------- + * boot_openrel + * ---------------- + */ + /* + 这个函数的作用是打开一个关系(relation)。函数的输入参数是一个指向关系名的字符串。 +首先,函数检查关系名的长度是否超过了NAMEDATALEN。如果超过了,就将字符串的最后一个字符设置为'\0',这样就能确保字符串的长度不会超过NAMEDATALEN。 +然后,函数检查全局变量t_thrd.bootstrap_cxt.Typ是否为NULL。如果是NULL,说明还没有加载pg_type数据,需要加载该数据。 +加载pg_type数据的过程是从pg_type表中获取所有的行,并将行的数量存储到变量i中。然后,根据行的数量动态分配空间,并将空间的指针赋值给变量app。 +然后,循环执行i次,每次分配一个typmap结构的空间,并将该空间的指针存储到指针数组app中。最后,将数组的最后一个元素设置为NULL。 +接下来,重新开始扫描pg_type表,将扫描的结果存储到tup中。 +然后,将当前typmap结构的am_oid成员设置为tup的OID属性值,将am_typ成员设置为tup的实际数据,并将指针app递增1。循环扫描表的每一行,直到扫描结束。 +最后,关闭pg_type表,并将全局变量t_thrd.bootstrap_cxt.boot_reldesc设置为新打开的关系。获取关系的属性数量,并为每个属性分配一个空间。 +将关系的属性数据复制到属性空间中,并输出一些调试信息。 +这个函数的作用是打开一个关系,并加载关系的属性信息。 +加载属性信息的过程中,需要先加载pg_type表中的数据,并将数据存储到全局变量t_thrd.bootstrap_cxt.Typ中。 +然后根据关系的名称,打开关系并获取属性数量,为每个属性分配空间,并复制属性数据到空间中。 + */ +void boot_openrel(char* relname) +{ + int i; + struct typmap** app; + Relation rel; + TableScanDesc scan; + HeapTuple tup; + errno_t rc; + + if (strlen(relname) >= NAMEDATALEN) + relname[NAMEDATALEN - 1] = '\0'; + + if (t_thrd.bootstrap_cxt.Typ == NULL) { + /* We can now load the pg_type data */ + rel = heap_open(TypeRelationId, NoLock); + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + i = 0; + + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) + ++i; + tableam_scan_end(scan); + app = t_thrd.bootstrap_cxt.Typ = ALLOC(struct typmap*, i + 1); + while (i-- > 0) + *app++ = ALLOC(struct typmap, 1); + *app = NULL; + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + app = t_thrd.bootstrap_cxt.Typ; + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) { + (*app)->am_oid = HeapTupleGetOid(tup); + rc = + memcpy_s((char*)&(*app)->am_typ, sizeof((*app)->am_typ), (char*)GETSTRUCT(tup), sizeof((*app)->am_typ)); + securec_check(rc, "\0", "\0"); + app++; + } + tableam_scan_end(scan); + heap_close(rel, NoLock); + } + + if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) + closerel(NULL); + + ereport(DEBUG4, (errmsg("open relation %s, attrsize %d", relname, (int)ATTRIBUTE_FIXED_PART_SIZE))); + + t_thrd.bootstrap_cxt.boot_reldesc = heap_openrv(makeRangeVar(NULL, relname, -1), NoLock); + t_thrd.bootstrap_cxt.numattr = RelationGetNumberOfAttributes(t_thrd.bootstrap_cxt.boot_reldesc); + for (i = 0; i < t_thrd.bootstrap_cxt.numattr; i++) { + if (t_thrd.bootstrap_cxt.attrtypes[i] == NULL) + t_thrd.bootstrap_cxt.attrtypes[i] = AllocateAttribute(); + rc = memmove_s((char*)t_thrd.bootstrap_cxt.attrtypes[i], + ATTRIBUTE_FIXED_PART_SIZE, + (char*)t_thrd.bootstrap_cxt.boot_reldesc->rd_att->attrs[i], + ATTRIBUTE_FIXED_PART_SIZE); + securec_check(rc, "\0", "\0"); + + { + Form_pg_attribute at = t_thrd.bootstrap_cxt.attrtypes[i]; + + ereport(DEBUG4, + (errmsg("create attribute %d name %s len %d num %d type %u", + i, + NameStr(at->attname), + at->attlen, + at->attnum, + at->atttypid))); + } + } +} + +/* ---------------- + * closerel + * ---------------- + */ +给函数closerel添加注释: + +/** + * @brief 关闭指定的关系。 + * + * @param name 要关闭的关系的名称。 + */ +void closerel(char* name) +{ + // 检查是否传入了正确的参数 + if (name != NULL) { + // 检查是否存在已打开的关系 + if (t_thrd.bootstrap_cxt.boot_reldesc) { + // 检查要关闭的关系名是否与当前已打开的关系名不同 + if (strcmp(RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc), name) != 0) + // 如果不同,报错,提示预期的关系名和实际的关系名 + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("close of %s when %s was expected", + name, + RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc)))); + } else { + // 如果不存在已打开的关系,报错,提示关闭关系前未打开任何关系 + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("close of %s before any relation was opened", name))); + } + } + + // 检查是否存在已打开的关系 + if (t_thrd.bootstrap_cxt.boot_reldesc == NULL) + // 如果不存在已打开的关系,报错,提示没有可关闭的关系 + ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("no open relation to close"))); + else { + // 输出调试信息,关闭关系,将已打开的关系指针设置为NULL + ereport(DEBUG4, (errmsg("close relation %s", RelationGetRelationName(t_thrd.bootstrap_cxt.boot_reldesc)))); + heap_close(t_thrd.bootstrap_cxt.boot_reldesc, NoLock); + t_thrd.bootstrap_cxt.boot_reldesc = NULL; + } +} + +/* +* fix roluseft ,rolmonitoradmin, roloperatoradmin and rolpolicyadmin column of pg_authid to notnull +*/ +static void fix_attr_notnull(const char* name, int attnum) +{ + if (strncmp(name, "roluseft", strlen("roluseft")) == 0 && strlen(name) == strlen("roluseft")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + if (strncmp(name, "rolmonitoradmin", strlen("rolmonitoradmin")) == 0 && strlen(name) == strlen("rolmonitoradmin")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + if (strncmp(name, "roloperatoradmin", strlen("roloperatoradmin")) == 0 && + strlen(name) == strlen("roloperatoradmin")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + if (strncmp(name, "rolpolicyadmin", strlen("rolpolicyadmin")) == 0 && strlen(name) == strlen("rolpolicyadmin")) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } +} + +/* ---------------- + * DEFINEATTR() + * + * define a pair + * if there are n fields in a relation to be created, this routine + * will be called n times + * ---------------- + */ + /* + 这个函数是用来定义数据库表中的一个属性(列)。函数的参数包括属性的名称(name)、数据类型(type)、以及属性的位置(attnum)。 +函数首先检查是否存在正在处理的关系(表),如果有,则发出警告并关闭该关系。 +接下来,函数分配一个新的Attribute结构体给t_thrd.bootstrap_cxt.attrtypes[attnum],并用0填充这个结构体。 +然后,函数将给定的属性名称和类型复制到Attribute结构体中,并设置了其他关于属性的一些信息,如编号(attnum + 1)、数据类型的OID值、数据类型的长度、是否将数据类型存储为基本类型等。 +接下来,函数会判断属性是否可以为空。如果属性是一个固定宽度的数据类型,或者前面的属性也是不可为空的变量(用C结构体声明访问),则将属性标记为"not null"。 +接着,函数检查特定的属性名称,并将这些属性标记为可为空或不可为空。 +这里列举了两个示例情况,第一个是将名为"partkey"、"intervaltablespace"、"intspnum"的属性标记为可为空,第二个是将名为"roluseft"、"rolmonitoradmin"、"roloperatoradmin"和"rolpolicyadmin"的属性标记为不可为空。 +总之,这个函数用于定义数据库表中的一个属性,包括属性的名称、数据类型和是否可为空等信息,并根据一些规则来判断是否将属性标记为"not null"。 + */ +void DefineAttr(const char* name, char* type, int attnum) +{ + Oid typeoid; + + if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) { + ereport(WARNING, (errmsg("no open relations allowed with CREATE command"))); + closerel(NULL); + } + + if (t_thrd.bootstrap_cxt.attrtypes[attnum] == NULL) + t_thrd.bootstrap_cxt.attrtypes[attnum] = AllocateAttribute(); + MemSet(t_thrd.bootstrap_cxt.attrtypes[attnum], 0, ATTRIBUTE_FIXED_PART_SIZE); + + (void)namestrcpy(&t_thrd.bootstrap_cxt.attrtypes[attnum]->attname, name); + ereport(DEBUG4, (errmsg("column %s %s", NameStr(t_thrd.bootstrap_cxt.attrtypes[attnum]->attname), type))); + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnum = attnum + 1; /* fillatt */ + + typeoid = gettype(type); + + if (t_thrd.bootstrap_cxt.Typ != NULL) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypid = t_thrd.bootstrap_cxt.Ap->am_oid; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen = t_thrd.bootstrap_cxt.Ap->am_typ.typlen; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attbyval = t_thrd.bootstrap_cxt.Ap->am_typ.typbyval; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attstorage = t_thrd.bootstrap_cxt.Ap->am_typ.typstorage; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attalign = t_thrd.bootstrap_cxt.Ap->am_typ.typalign; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attcollation = t_thrd.bootstrap_cxt.Ap->am_typ.typcollation; + /* if an array type, assume 1-dimensional attribute */ + if (t_thrd.bootstrap_cxt.Ap->am_typ.typelem != InvalidOid && t_thrd.bootstrap_cxt.Ap->am_typ.typlen < 0) + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 1; + else + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 0; + } else { + t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypid = TypInfo[typeoid].oid; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen = TypInfo[typeoid].len; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attbyval = TypInfo[typeoid].byval; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attstorage = TypInfo[typeoid].storage; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attalign = TypInfo[typeoid].align; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attcollation = TypInfo[typeoid].collation; + /* if an array type, assume 1-dimensional attribute */ + if (TypInfo[typeoid].elem != InvalidOid && t_thrd.bootstrap_cxt.attrtypes[attnum]->attlen < 0) + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 1; + else + t_thrd.bootstrap_cxt.attrtypes[attnum]->attndims = 0; + } + + t_thrd.bootstrap_cxt.attrtypes[attnum]->attstattarget = -1; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attcacheoff = -1; + t_thrd.bootstrap_cxt.attrtypes[attnum]->atttypmod = -1; + t_thrd.bootstrap_cxt.attrtypes[attnum]->attislocal = true; + + /* + * Mark as "not null" if type is fixed-width and prior columns are too. + * This corresponds to case where column can be accessed directly via C + * struct declaration. + * + * oidvector and int2vector are also treated as not-nullable, even though + * they are no longer fixed-width. + */ +#define MARKNOTNULL(att) ((att)->attlen > 0 || (att)->atttypid == OIDVECTOROID || (att)->atttypid == INT2VECTOROID) + + if (MARKNOTNULL(t_thrd.bootstrap_cxt.attrtypes[attnum])) { + int i; + + for (i = 0; i < attnum; i++) { + if (!MARKNOTNULL(t_thrd.bootstrap_cxt.attrtypes[i])) + break; + } + if (i == attnum) + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = true; + } + + // fix partkey/intervaltablespace/intspnum columns of pg_partition to nullable + if (strcmp(name, "partkey") == 0 || strcmp(name, "intervaltablespace") == 0 || strcmp(name, "intspnum") == 0) { + t_thrd.bootstrap_cxt.attrtypes[attnum]->attnotnull = false; + } + + // fix roluseft ,rolmonitoradmin, roloperatoradmin and rolpolicyadmin column of pg_authid to notnull + fix_attr_notnull(name, attnum); + +} + +/* ---------------- + * InsertOneTuple + * + * If objectid is not zero, it is a specific OID to assign to the tuple. + * Otherwise, an OID will be assigned (if necessary) by heap_insert. + * ---------------- + */ + /*这个函数用于向表中插入一条元组。函数的参数是一个对象ID(objectid),表示要插入的元组的ID。函数通过`t_thrd.bootstrap_cxt.boot_reldesc`访问引导过程中的关系(表)描述符。 +首先,函数打印调试信息,包括要插入的行的ID和列数。然后,函数检查关系(表)描述符是否为引导过程中的pg_proc表的描述符,如果是,则报错,因为内置函数不应该被添加到pg_proc表中。 +接下来,函数调用`CreateTupleDesc`函数创建一个描述插入元组的元组描述符(tupDesc)。创建元组描述符时传递了一些参数,包括属性数目、关系是否具有物理上标识的Oid、属性类型数组以及关系的类型。 +然后,函数调用`tableam_tops_form_tuple`函数创建一个HeapTuple结构体,即要插入的元组。创建HeapTuple时使用了`tupDesc`、`values`(待插入的属性值数组)以及`Nulls`(表示每个属性是否为NULL的标记)。 +如果传递了非0的对象ID(objectid),则使用`HeapTupleSetOid`函数设置HeapTuple的对象ID。 +接下来,函数通过调用`simple_heap_insert`函数将HeapTuple插入到关系中。 +然后,函数通过调用`tableam_tops_free_tuple`函数释放之前创建的HeapTuple。 +最后,函数在插入完成后打印调试信息,并通过循环将`Nulls`数组重置为false,以便下一次插入元组时使用。 +总之,这个函数用于插入一条元组到表中。函数创建一个插入元组的元组描述符,然后根据传递的属性值和标记创建一个HeapTuple,并将其插入到关系中。最后,函数释放已创建的HeapTuple,并重置标记数组以备下次插入使用。*/ +void InsertOneTuple(Oid objectid) +{ + HeapTuple tuple; + TupleDesc tupDesc; + int i; + + ereport(DEBUG4, (errmsg("inserting row oid %u, %d columns", objectid, t_thrd.bootstrap_cxt.numattr))); + + if (IsBootingPgProc(t_thrd.bootstrap_cxt.boot_reldesc)) { + ereport(FATAL, (errmsg("Built-in functions should not be added into pg_proc"))); + } + tupDesc = CreateTupleDesc(t_thrd.bootstrap_cxt.numattr, + RelationGetForm(t_thrd.bootstrap_cxt.boot_reldesc)->relhasoids, + t_thrd.bootstrap_cxt.attrtypes, + t_thrd.bootstrap_cxt.boot_reldesc->rd_tam_type); + tuple = (HeapTuple) tableam_tops_form_tuple(tupDesc, values, Nulls, HEAP_TUPLE); + if (objectid != (Oid)0) + HeapTupleSetOid(tuple, objectid); + pfree(tupDesc); /* just free's tupDesc, not the attrtypes */ + + (void)simple_heap_insert(t_thrd.bootstrap_cxt.boot_reldesc, tuple); + tableam_tops_free_tuple(tuple); + ereport(DEBUG4, (errmsg("row inserted"))); + + /* + * Reset null markers for next tuple + */ + for (i = 0; i < t_thrd.bootstrap_cxt.numattr; i++) + Nulls[i] = false; +} + +/* ---------------- + * InsertOneValue + * ---------------- + */ +void InsertOneValue(char* value, int i) +{ + Oid typoid; + int16 typlen; + bool typbyval = false; + char typalign; + char typdelim; + Oid typioparam; + Oid typinput; + Oid typoutput; + char* prt = NULL; + + AssertArg(i >= 0 && i < MAXATTR); + + ereport(DEBUG4, (errmsg("inserting column %d value \"%s\"", i, value))); + + typoid = t_thrd.bootstrap_cxt.boot_reldesc->rd_att->attrs[i]->atttypid; + + boot_get_type_io_data(typoid, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typinput, &typoutput); + + values[i] = OidInputFunctionCall(typinput, value, typioparam, -1); + prt = OidOutputFunctionCall(typoutput, values[i]); + ereport(DEBUG4, (errmsg("inserted -> %s", prt))); + pfree(prt); +} + +/* ---------------- + * InsertOneNull + * ---------------- + */ +void InsertOneNull(int i) +{ + ereport(DEBUG4, (errmsg("inserting column %d NULL", i))); + Assert(i >= 0 && i < MAXATTR); + values[i] = PointerGetDatum(NULL); + Nulls[i] = true; +} + +/* ---------------- + * cleanup + * ---------------- + */ +static void cleanup(void) +{ + if (t_thrd.bootstrap_cxt.boot_reldesc != NULL) + closerel(NULL); +} + +/* ---------------- + * gettype + * + * NB: this is really ugly; it will return an integer index into TypInfo[], + * and not an OID at all, until the first reference to a type not known in + * TypInfo[]. At that point it will read and cache pg_type in the Typ array, + * and subsequently return a real OID (and set the global pointer Ap to + * point at the found row in Typ). So caller must check whether Typ is + * still NULL to determine what the return value is! + * ---------------- + */ +static Oid gettype(char* type) +{ + int i; + Relation rel; + TableScanDesc scan; + HeapTuple tup; + struct typmap** app; + errno_t rc; + + if (t_thrd.bootstrap_cxt.Typ != NULL) { + for (app = t_thrd.bootstrap_cxt.Typ; *app != NULL; app++) { + if (strncmp(NameStr((*app)->am_typ.typname), type, NAMEDATALEN) == 0) { + t_thrd.bootstrap_cxt.Ap = *app; + return (*app)->am_oid; + } + } + } else { + for (i = 0; i < n_types; i++) { + if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0) + return i; + } + ereport(DEBUG4, (errmsg("external type: %s", type))); + rel = heap_open(TypeRelationId, NoLock); + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + i = 0; + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) + ++i; + tableam_scan_end(scan); + app = t_thrd.bootstrap_cxt.Typ = ALLOC(struct typmap*, i + 1); + while (i-- > 0) + *app++ = ALLOC(struct typmap, 1); + *app = NULL; + scan = tableam_scan_begin(rel, SnapshotNow, 0, NULL); + app = t_thrd.bootstrap_cxt.Typ; + while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) { + (*app)->am_oid = HeapTupleGetOid(tup); + rc = memmove_s( + (char*)&(*app++)->am_typ, sizeof((*app)->am_typ), (char*)GETSTRUCT(tup), sizeof((*app)->am_typ)); + securec_check(rc, "\0", "\0"); + } + tableam_scan_end(scan); + heap_close(rel, NoLock); + return gettype(type); + } + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unrecognized type \"%s\"", type))); + /* not reached, here to make compiler happy */ + return 0; +} + +/* ---------------- + * boot_get_type_io_data + * + * Obtain type I/O information at bootstrap time. This intentionally has + * almost the same API as lsyscache.c's get_type_io_data, except that + * we only support obtaining the typinput and typoutput routines, not + * the binary I/O routines. It is exported so that array_in and array_out + * can be made to work during early bootstrap. + * ---------------- + */ + /* + 这个函数用于获取指定类型(typid)的输入/输出数据。函数通过指定的typid查找对应的类型信息,并将这些信息通过输出参数返回给调用者。 +首先,函数检查`t_thrd.bootstrap_cxt.Typ`是否为空。如果不为空,说明在引导过程中已经获取到了`pg_type`表的内容。接下来,函数在`t_thrd.bootstrap_cxt.Typ`中查找指定的typid对应的类型信息,并将找到的信息保存在`ap`结构体中。 +如果没有找到指定的typid对应的信息,则报错,提示类型OID未在Typ列表中找到。 +接着,函数通过将`ap`结构体中的成员值赋给输出参数,将类型信息返回给调用者。具体包括类型的长度(typlen)、是否传递值(typbyval)、对齐方式(typalign)、分隔符(typdelim)、类型输入函数参数(typioparam)、类型输入函数(typinput)、类型输出函数(typoutput)。 +如果`t_thrd.bootstrap_cxt.Typ`为空,说明还没有获取到`pg_type`表的内容。在这种情况下,函数将使用固定的`TypInfo`数组来获取类型信息。函数通过遍历`TypInfo`数组,查找指定typid对应的类型信息,并将找到的信息保存在`typeindex`变量中。 +如果找不到指定typid对应的类型信息,则报错,提示类型OID在TypInfo中未找到。 +接下来,函数通过将`TypInfo`数组中的成员值赋给输出参数,将类型信息返回给调用者。具体包括类型的长度(typlen)、是否传递值(typbyval)、对齐方式(typalign)、分隔符(typdelim)、类型输入函数参数(typioparam)、类型输入函数(typinput)、类型输出函数(typoutput)。 +总之,这个函数用于获取指定类型的输入/输出数据。函数根据是否已经获取到`pg_type`表的内容来决定是使用`pg_type`中的类型信息,还是使用固定的`TypInfo`数组中的类型信息。然后,函数将获取到的类型信息通过输出参数返回给调用者。 + */ +void boot_get_type_io_data(Oid typid, int16* typlen, bool* typbyval, char* typalign, char* typdelim, Oid* typioparam, + Oid* typinput, Oid* typoutput) +{ + if (t_thrd.bootstrap_cxt.Typ != NULL) { + /* We have the boot-time contents of pg_type, so use it */ + struct typmap** app; + struct typmap* ap = NULL; + + app = t_thrd.bootstrap_cxt.Typ; + while (*app && (*app)->am_oid != typid) + ++app; + ap = *app; + if (ap == NULL) + ereport( + ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("type OID %u not found in Typ list", typid))); + + *typlen = ap->am_typ.typlen; + *typbyval = ap->am_typ.typbyval; + *typalign = ap->am_typ.typalign; + *typdelim = ap->am_typ.typdelim; + + /* XXX this logic must match getTypeIOParam() */ + if (OidIsValid(ap->am_typ.typelem)) + *typioparam = ap->am_typ.typelem; + else + *typioparam = typid; + + *typinput = ap->am_typ.typinput; + *typoutput = ap->am_typ.typoutput; + } else { + /* We don't have pg_type yet, so use the hard-wired TypInfo array */ + int typeindex; + + for (typeindex = 0; typeindex < n_types; typeindex++) { + if (TypInfo[typeindex].oid == typid) + break; + } + if (typeindex >= n_types) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("type OID %u not found in TypInfo", typid))); + + *typlen = TypInfo[typeindex].len; + *typbyval = TypInfo[typeindex].byval; + *typalign = TypInfo[typeindex].align; + /* We assume typdelim is ',' for all boot-time types */ + *typdelim = ','; + + /* XXX this logic must match getTypeIOParam() */ + if (OidIsValid(TypInfo[typeindex].elem)) + *typioparam = TypInfo[typeindex].elem; + else + *typioparam = typid; + + *typinput = TypInfo[typeindex].inproc; + *typoutput = TypInfo[typeindex].outproc; + } +} + +/* ---------------- + * AllocateAttribute + * + * Note: bootstrap never sets any per-column ACLs, so we only need + * ATTRIBUTE_FIXED_PART_SIZE space per attribute. + * ---------------- + */ +static Form_pg_attribute AllocateAttribute(void) +{ + Form_pg_attribute attribute = (Form_pg_attribute)MemoryContextAlloc( + SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), ATTRIBUTE_FIXED_PART_SIZE); + + if (!PointerIsValid(attribute)) + ereport(FATAL, (errmsg("out of memory"))); + MemSet(attribute, 0, ATTRIBUTE_FIXED_PART_SIZE); + + return attribute; +} + +/* ---------------- + * MapArrayTypeName + * XXX arrays of "basetype" are always "_basetype". + * this is an evil hack inherited from rel. 3.1. + * XXX array dimension is thrown away because we + * don't support fixed-dimension arrays. again, + * sickness from 3.1. + * + * the string passed in must have a '[' character in it + * + * the string returned is a pointer to static storage and should NOT + * be freed by the CALLER. + * ---------------- + */ +const char* MapArrayTypeName(const char* s) +{ + int i; + int j; + + if (s == NULL || s[0] == '\0') + return s; + + j = 1; + t_thrd.bootstrap_cxt.newStr[0] = '_'; + for (i = 0; i < NAMEDATALEN - 1 && s[i] != '['; i++, j++) + t_thrd.bootstrap_cxt.newStr[j] = s[i]; + + t_thrd.bootstrap_cxt.newStr[j] = '\0'; + + return t_thrd.bootstrap_cxt.newStr; +} + +/* + * index_register() -- record an index that has been set up for building + * later. + * + * At bootstrap time, we define a bunch of indexes on system catalogs. + * We postpone actually building the indexes until just before we're + * finished with initialization, however. This is because the indexes + * themselves have catalog entries, and those have to be included in the + * indexes on those catalogs. Doing it in two phases is the simplest + * way of making sure the indexes have the right contents at the end. + */ + /*这个函数用于在引导过程中注册索引。函数接收三个参数:heap(堆表的对象ID)、ind(索引的对象ID)和indexInfo(IndexInfo结构体的指针,包含了索引的详细信息)。 +函数。首先,函数创建一个IndexList结构体的实例newind,并将其初始化为NULL。 +接下来,函数检查是否已经创建了t_thrd.bootstrap_cxt.nogc上下文,如果没有,则创建一个名为"BootstrapNoGC"的上下文。这个上下文用于在引导过程中暂时保存索引的相关信息,防止其被垃圾回收。 +然后函数将当前的内存上下文切换到t_thrd.bootstrap_cxt.nogc上下文。 +接着,函数分配一个IndexList结构体的内存,并将heap、ind和indexInfo的值分别赋给新分配的结构体的相应成员变量。 +然后函数通过memcpy_s函数将indexInfo结构体的内容复制到newind->il_info中。同时,函数使用copyObject函数分别复制indexInfo->ii_Expressions和indexInfo->ii_Predicate,并将复制后的值分别赋给newind->il_info->ii_Expressions和newind->il_info->ii_Predicate。 +接下来,函数将newind添加到t_thrd.bootstrap_cxt.ILHead链表中,以便在稍后的操作中使用。 +最后,函数将内存上下文切换回先前的上下文。 +总之,这个函数用于在引导过程中注册索引。它创建一个表示索引的IndexList结构体对象,并将索引相关的信息保存在其中。然后,它将这个对象添加到上下文链表中以备后续使用。*/ +void index_register(Oid heap, Oid ind, IndexInfo* indexInfo) +{ + IndexList* newind = NULL; + MemoryContext oldcxt; + errno_t rc; + + /* + * XXX mao 10/31/92 -- don't gc index reldescs, associated info at + * bootstrap time. we'll declare the indexes now, but want to create them + * later. + */ + if (t_thrd.bootstrap_cxt.nogc == NULL) + t_thrd.bootstrap_cxt.nogc = AllocSetContextCreate( + NULL, "BootstrapNoGC", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); + + oldcxt = MemoryContextSwitchTo(t_thrd.bootstrap_cxt.nogc); + + newind = (IndexList*)palloc(sizeof(IndexList)); + newind->il_heap = heap; + newind->il_ind = ind; + newind->il_info = (IndexInfo*)palloc(sizeof(IndexInfo)); + + rc = memcpy_s(newind->il_info, sizeof(IndexInfo), indexInfo, sizeof(IndexInfo)); + securec_check(rc, "\0", "\0"); + /* expressions will likely be null, but may as well copy it */ + newind->il_info->ii_Expressions = (List*)copyObject(indexInfo->ii_Expressions); + newind->il_info->ii_ExpressionsState = NIL; + /* predicate will likely be null, but may as well copy it */ + newind->il_info->ii_Predicate = (List*)copyObject(indexInfo->ii_Predicate); + newind->il_info->ii_PredicateState = NIL; + /* no exclusion constraints at bootstrap time, so no need to copy */ + Assert(indexInfo->ii_ExclusionOps == NULL); + Assert(indexInfo->ii_ExclusionProcs == NULL); + Assert(indexInfo->ii_ExclusionStrats == NULL); + + newind->il_next = t_thrd.bootstrap_cxt.ILHead; + t_thrd.bootstrap_cxt.ILHead = newind; + + (void)MemoryContextSwitchTo(oldcxt); +} + +/* + * build_indices -- fill in all the indexes registered earlier + */ + /* + 这个函数用于在引导过程中构建索引。它通过遍历t_thrd.bootstrap_cxt.ILHead链表中的每个索引来逐个构建索引。 +循环的迭代条件是t_thrd.bootstrap_cxt.ILHead不为空,也就是说还有待构建的索引。循环的每次迭代,我们会定义两个Relation对象:heap和ind,分别用于表示堆表和索引表。 +引导过程中不需要考虑获取锁的问题,所以我们使用heap_open和index_open函数打开堆表和索引表。这两个函数接收两个参数:表的对象ID和锁的模式(NoLock表示不获取锁)。返回的Relation对象分别赋给heap和ind变量。 +然后,我们调用index_build函数来构建索引。这个函数接收多个参数,包括堆表、分区信息、索引表、并行标志、索引详细信息等。这些参数的值分别来自t_thrd.bootstrap_cxt.ILHead链表的当前节点。函数会使用这些参数来构建索引。 +索引构建完成后,我们使用index_close和heap_close函数关闭索引表和堆表。这些函数同样需要传入锁的模式参数(NoLock)。 +总之,这个函数用于在引导过程中构建索引。通过遍历t_thrd.bootstrap_cxt.ILHead链表中的每个索引,我们依次打开堆表和索引表,并调用index_build函数进行索引构建。最后,我们关闭索引表和堆表。 + */ +void build_indices(void) +{ + for (; t_thrd.bootstrap_cxt.ILHead != NULL; t_thrd.bootstrap_cxt.ILHead = t_thrd.bootstrap_cxt.ILHead->il_next) { + Relation heap; + Relation ind; + + /* need not bother with locks during bootstrap */ + heap = heap_open(t_thrd.bootstrap_cxt.ILHead->il_heap, NoLock); + ind = index_open(t_thrd.bootstrap_cxt.ILHead->il_ind, NoLock); + index_build( + heap, NULL, ind, NULL, t_thrd.bootstrap_cxt.ILHead->il_info, false, false, INDEX_CREATE_NONE_PARTITION); + + index_close(ind, NoLock); + heap_close(heap, NoLock); + } +} -- 2.34.1 From 6e3d6f09d442f23c65d0cb0f392e3d528eb3061c Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:14:20 +0800 Subject: [PATCH 18/56] Delete 'src/gausskernel/cbb/bbox/bbox_create.cpp' --- src/gausskernel/cbb/bbox/bbox_create.cpp | 517 ----------------------- 1 file changed, 517 deletions(-) delete mode 100644 src/gausskernel/cbb/bbox/bbox_create.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_create.cpp b/src/gausskernel/cbb/bbox/bbox_create.cpp deleted file mode 100644 index 447688eb5..000000000 --- a/src/gausskernel/cbb/bbox/bbox_create.cpp +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * bbox_create.cpp - * - * IDENTIFICATION - * src/gausskernel/cbb/bbox/bbox_create.cpp - * - * ------------------------------------------------------------------------- - */ -#include "bbox_elf_dump_base.h" -#include "bbox_create.h" -#include "bbox.h" -#include "../../src/include/securec.h" -#include "../../src/include/securec_check.h" - -/* Core path of the bbox process */ -char g_szBboxCorePath[BBOX_NAME_PATH_LEN] = "./"; - -/* count of the BBOX process */ -s32 g_iBBoxCoreFileCount = 30; - -/* the max core file size of bbox process */ -s32 g_iBBoxCoreFileSize = 50; - -long int g_iCoreDumpBeginTime = 0; -long int g_iCoreDumpEndTime = 0; - -char g_acDateTime[BBOX_TINE_LEN]; - -/* - * get system date time. - * return 0 if sucess else err code. - */ -int BBOX_GetSysDateTime(void) -{ - int iCommandFD = -1; - int iReadSize = 0; - - BBOX_NOINTR(iCommandFD = sys_popen(BBOX_DATE_TIME_CMD, "r")); - if (iCommandFD < 0) { - bbox_print(PRINT_ERR, "sys_popen is failed, errno = %d.\n", errno); - return RET_ERR; - } - - /* read the result of sys_read command */ - BBOX_NOINTR(iReadSize = sys_read(iCommandFD, g_acDateTime, BBOX_TINE_LEN)); - if (iReadSize <= 0) { - (void)sys_pclose(iCommandFD); - bbox_print(PRINT_ERR, "sys_read is failed, iReadSize = %d.\n", iReadSize); - return RET_ERR; - } - - (void)sys_pclose(iCommandFD); - - if (iReadSize > 0) { - g_acDateTime[iReadSize - 1] = '\0'; /* remove '\n' */ - } - - bbox_print(PRINT_LOG, "Get system time %s.\n", g_acDateTime); - - return RET_OK; -} - -/* - * get default core file name - * return 0 if sucess else err code. - */ -s32 BBOX_GetDefaultCoreName(char* szFileName, s32 iSize, const char* szAddText) -{ - if (szFileName == NULL) { - return RET_ERR; - } - - if (szAddText == NULL) { - szAddText = ""; - } - - struct kernel_timeval stProgramCoreDumpTime = {0}; - sys_gettimeofday(&stProgramCoreDumpTime, NULL); - - s32 ret = bbox_snprintf(szFileName, - iSize, - "%s/core-%s-%d-%s-%s.lz4", - g_szBboxCorePath, - progname, - sys_getpid(), - g_acDateTime, - szAddText); - - return (ret > 0) ? RET_OK : RET_ERR; -} - -/* - * get temp core file name - * return 0 if sucess else err code. - */ -s32 BBOX_GetTmpCoreName(char* szFileName, s32 iSize) -{ - if (szFileName == NULL) { - return RET_ERR; - } - - struct kernel_timeval stProgramCoreDumpTime = {0}; - sys_gettimeofday(&stProgramCoreDumpTime, NULL); - - g_iCoreDumpBeginTime = stProgramCoreDumpTime.tv_sec; - bbox_print(PRINT_TIP, "coredump begin at %ld\n", stProgramCoreDumpTime.tv_sec); - - s32 ret = bbox_snprintf(szFileName, - iSize, - "%s/%s-%d-%s-%s", - g_szBboxCorePath, - progname, - sys_getpid(), - g_acDateTime, - BBOX_TMP_FILE_ADD_NAME); - - return (ret > 0) ? RET_OK : RET_ERR; -} - -/* - * get core file count of bbox process. - * return 0 if sucess else err code. - */ -s32 BBOX_GetBBoxFileCount(const char* pszPath, const char* pszName, void* pArgs) -{ - s32* pIFileCount = NULL; - - pIFileCount = (s32*)pArgs; - if (pIFileCount == NULL) { - bbox_print(PRINT_ERR, "Invalid argument pstArgs\n"); - return RET_ERR; - } - - /* ignore path "." and ".." */ - if ((0 == bbox_strcmp(pszName, ".")) || (0 == bbox_strcmp(pszName, ".."))) { - return RET_OK; - } - - /* ignore the file which not belong to bbox */ - if ((0 == bbox_strstr(pszName, BBOX_SNAP_FILE_ADD_NAME ".lz4")) && - (0 == bbox_strstr(pszName, BBOX_CORE_FILE_ADD_NAME ".lz4"))) { - return RET_OK; - } - - /* ignore the file which not created by this process. */ - if (bbox_strstr(pszName, progname) == 0) { - return RET_OK; - } - - /* file count ++ */ - *pIFileCount = *pIFileCount + 1; - - return RET_OK; -} - -/* - * get bbox ildiest name. - * return 0 if sucess else err code. - */ -s32 BBOX_GetBBoxOldiestName(const char* pszPath, const char* pszName, void* pArgs) -{ - struct BBOX_ListDirParam* pstArgs = NULL; - struct kernel_stat* pstOldiestState = NULL; - char* pszOldName = NULL; - struct kernel_stat stCurrState = {0}; - char szFileName[BBOX_NAME_PATH_LEN]; - - pstArgs = (struct BBOX_ListDirParam*)pArgs; - if (pstArgs == NULL) { - bbox_print(PRINT_ERR, "Invalid argument pstArgs\n"); - return RET_ERR; - } - - pstOldiestState = (struct kernel_stat*)pstArgs->pArg1; - pszOldName = (char*)pstArgs->pArg2; - - /* ignore path "." and ".." */ - if ((0 == bbox_strcmp(pszName, ".")) || (0 == bbox_strcmp(pszName, ".."))) { - return RET_OK; - } - - /* ignore the file which not belong to bbox */ - if ((0 == bbox_strstr(pszName, BBOX_SNAP_FILE_ADD_NAME ".lz4")) && - (0 == bbox_strstr(pszName, BBOX_CORE_FILE_ADD_NAME ".lz4"))) { - return RET_OK; - } - - /* ignore the file which not created by this process. */ - if (bbox_strstr(pszName, progname) == 0) { - return RET_OK; - } - - if (bbox_snprintf(szFileName, sizeof(szFileName), "%s/%s", pszPath, pszName) <= 0) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - return RET_ERR; - } - - if (sys_stat(szFileName, &stCurrState) < 0) { - bbox_print(PRINT_ERR, "Get stat of '%s' failed, errno = %d\n", szFileName, errno); - return RET_ERR; - } - - /* compare and judge if it is the oldiest time */ - if (stCurrState.st_mtime_ < pstOldiestState->st_mtime_) { - /* record the oldiest file information */ - *pstOldiestState = stCurrState; - if (bbox_snprintf(pszOldName, BBOX_NAME_PATH_LEN, "%s/%s", pszPath, pszName) <= 0) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - return RET_ERR; - } - } - - return RET_OK; -} - -/* - * remove the oldiest bbox file. - */ -void BBOX_RemoveOldiestBBoxFile(void) -{ - s32 iRet = 0; - struct kernel_stat stOldiestState; - struct BBOX_ListDirParam stArgs = {0}; - char szFileName[BBOX_NAME_PATH_LEN]; - errno_t rc = EOK; - - /* set the file modified time to the max value acquiescently. */ - rc = memset_s(&stOldiestState, sizeof(stOldiestState), 0xFF, sizeof(stOldiestState)); - securec_check_c(rc, "\0", "\0"); - - stOldiestState.st_size = -1; - - stArgs.pArg1 = &stOldiestState; - stArgs.pArg2 = szFileName; - - /* search the oldiest file. */ - iRet = bbox_listdir(g_szBboxCorePath, BBOX_GetBBoxOldiestName, &stArgs); - if (iRet != RET_OK) { - return; - } - - /* remove it if found. */ - if (-1 != stOldiestState.st_size) { - sys_unlink(szFileName); - } -} - -/* - * remove the old bbox file - */ -void BBOX_RemoveOldBBoxFile(void) -{ - s32 iFileCount = 0; - s32 iRet = 0; - s32 i; - - iRet = bbox_listdir(g_szBboxCorePath, BBOX_GetBBoxFileCount, &iFileCount); - if (iRet != RET_OK) { - bbox_print(PRINT_ERR, "Get bbox file count failed.\n"); - return; - } - - /* remove redundant file */ - for (i = 0; i < (iFileCount - g_iBBoxCoreFileCount + 1); i++) { - /* remove the oldiest bbox file. */ - BBOX_RemoveOldiestBBoxFile(); - } -} - -/* - * remove bbox temp file. - */ -s32 BBOX_DoRemoveTempBBoxFile(const char* pszPath, const char* pszName, void* pArgs) -{ - struct kernel_stat stCurrState = {0}; - struct kernel_timeval stCurrTime = {0}; - char szFileName[BBOX_NAME_PATH_LEN]; - - /* ignore path "." and ".." */ - if ((0 == bbox_strcmp(pszName, ".")) || (0 == bbox_strcmp(pszName, ".."))) { - return RET_OK; - } - - /* ignore the file which not belong to bbox */ - if (0 == bbox_strstr(pszName, BBOX_TMP_FILE_ADD_NAME)) { - return RET_OK; - } - - /* ignore the file which not created by this process. */ - if (0 != bbox_strncmp(pszName, progname, bbox_strlen(progname))) { - return RET_OK; - } - - if (bbox_snprintf(szFileName, sizeof(szFileName), "%s/%s", pszPath, pszName) <= 0) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - return RET_ERR; - } - - if (sys_stat(szFileName, &stCurrState) < 0) { - bbox_print(PRINT_ERR, "Get stat of '%s' failed, errno = %d\n", szFileName, errno); - return RET_ERR; - } - - /* get current time */ - sys_gettimeofday(&stCurrTime, NULL); - - /* remove temp file before BBOX_TMP_DEL_TIME_INTERVAL */ - if (stCurrState.st_mtime_ < (unsigned long)(stCurrTime.tv_sec - BBOX_TMP_DEL_TIME_INTERVAL)) { - /* rm file */ - bbox_print(PRINT_ERR, "Delete bad bbox core file %s \n", szFileName); - sys_unlink(szFileName); - } - - return RET_OK; -} - -/* - * remove temp bbox file - */ -void BBOX_RemoveTempBBoxFile(void) -{ - s32 iRet = 0; - - iRet = bbox_listdir(g_szBboxCorePath, BBOX_DoRemoveTempBBoxFile, NULL); - if (iRet != RET_OK) { - bbox_print(PRINT_ERR, "Get bbox file count failed.\n"); - return; - } -} - -/* - finish core dump file. - in : args defined by user and it is file name here. - -*/ -void BBOX_FinishDumpFile(void* args) -{ - char* pszNewName = NULL; - char* pszOldName = NULL; - struct kernel_timeval stProgramCoreDumpTime = {0}; - struct BBOX_ListDirParam* pstArgs = (struct BBOX_ListDirParam*)args; - - if (args == NULL) { - bbox_print(PRINT_ERR, "BBOX_FinishDumpFile args is null.\n"); - return; - } - - sys_gettimeofday(&stProgramCoreDumpTime, NULL); - - g_iCoreDumpEndTime = stProgramCoreDumpTime.tv_sec; - bbox_print(PRINT_TIP, "coredump End at %ld\n", stProgramCoreDumpTime.tv_sec); - - bbox_print(PRINT_TIP, "coredump used time: %ld sec\n", g_iCoreDumpEndTime - g_iCoreDumpBeginTime); - - /* remove oldiest file */ - BBOX_RemoveOldBBoxFile(); - - pszNewName = (char*)pstArgs->pArg1; - pszOldName = (char*)pstArgs->pArg2; - - /* change file mode to 0600 */ - if (sys_chmod(pszOldName, 0600)) { - bbox_print(PRINT_ERR, "set %s mode to 0600 failed, errno = %d\n", pszOldName, errno); - } - - /* rename file */ - if (pszNewName != NULL && pszOldName != NULL) { - if (sys_rename(pszOldName, pszNewName) < 0) { - bbox_print(PRINT_ERR, "rename file %s to %s failed, errno = %d.\n", pszOldName, pszNewName, errno); - } - } - - /* remove temp bbox file. */ - BBOX_RemoveTempBBoxFile(); -} - -/* - * create core dump file. - * return 0 if success else err code. - */ -s32 BBOX_CreateCoredump(char* file_name) -{ - s32 iRet = 0; - char szFileName[BBOX_NAME_PATH_LEN]; - char szTmpName[BBOX_NAME_PATH_LEN]; - struct BBOX_ListDirParam stArgs = {0}; - char* file_tmp = file_name; - FRAME(frame); - - bbox_initlog(0); - - bbox_print(PRINT_TIP, "\nBBOX LOG\n-------------------------------\n"); - - iRet = BBOX_GetSysDateTime(); - if (iRet != RET_OK) { - return RET_ERR; - } - - if (bbox_mkdir(g_szBboxCorePath) < 0) { - bbox_print(PRINT_ERR, "bbox_mkdir is failed, errno = %d.\n", errno); - return RET_ERR; - } - - /* if file_name is NULL, create it using default name. */ - if (file_name == NULL) { - if (BBOX_GetDefaultCoreName(szFileName, BBOX_NAME_PATH_LEN, BBOX_CORE_FILE_ADD_NAME) == RET_OK) { - file_name = szFileName; - } else { - bbox_print(PRINT_ERR, "BBOX_GetDefaultCoreName is failed, errno = %d.\n", errno); - return RET_ERR; - } - } - - bbox_print(PRINT_TIP, "core file path is %s\n", file_name); - - if (BBOX_GetTmpCoreName(szTmpName, BBOX_NAME_PATH_LEN) == RET_OK) { - file_tmp = szTmpName; - } else { - bbox_print(PRINT_ERR, "BBOX_GetTmpCoreName is failed, errno = %d.\n", errno); - return RET_ERR; - } - - stArgs.pArg1 = file_name; - stArgs.pArg2 = file_tmp; - - iRet = BBOX_GetAllThreads(GET_TYPE_DUMP, BBOX_FinishDumpFile, &stArgs, BBOX_DoDumpElfCore, &frame, file_tmp); - - return iRet; -} - -/* - * set core dump path - * return 0 if success else err code. - */ -s32 BBOX_SetCoredumpPath(const char* pszPath) -{ - if (NULL != pszPath) { - if (bbox_snprintf(g_szBboxCorePath, sizeof(g_szBboxCorePath), "%s", pszPath) <= 0) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - return RET_ERR; - } - } else { - return RET_ERR; - } - - if (0 == bbox_strlen(g_szBboxCorePath)) { - return RET_ERR; - } - - if (bbox_mkdir(g_szBboxCorePath) < 0) { - bbox_print(PRINT_ERR, "bbox_mkdir is failed, errno = %d.\n", errno); - return RET_ERR; - } - - return RET_OK; -} - -/* - * set core file count - * return 0 if success else err code. - */ -s32 BBOX_SetCoreFileCount(s32 iCount) -{ - if (iCount < 0) { - return RET_ERR; - } - - g_iBBoxCoreFileCount = iCount; - - return RET_OK; -} - -/* - * add an blacklist item to exclude it from core file. - * void *pAddress : the head address of excluded memory - * u64 uilen : memory size - * return RET_OK if success else RET_ERR. - */ -s32 BBOX_AddBlackListAddress(void* pAddress, u64 uiLen) -{ - if (pAddress == NULL || uiLen == 0) { - bbox_print(PRINT_ERR, "parameter pAddress(******) uiLen(%llu) is invaild.\n", uiLen); - return RET_ERR; - } - - return _BBOX_AddBlackListAddress(pAddress, uiLen); -} - -/* - * remove an blacklist item. - * void *pAddress : the head address of excluded memory - * return RET_OK if success else RET_ERR. - */ -s32 BBOX_RmvBlackListAddress(void* pAddress) -{ - if (pAddress == NULL) { - bbox_print(PRINT_ERR, "parameter pAddress(******) is invaild.\n"); - return RET_ERR; - } - - return _BBOX_RmvBlackListAddress(pAddress); -} - -- 2.34.1 From a75168ec6fa5e82fc7644d882728e35d6ed631dc Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:14:46 +0800 Subject: [PATCH 19/56] ADD file via upload --- src/gausskernel/cbb/bbox/bbox_create.cpp | 533 +++++++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 src/gausskernel/cbb/bbox/bbox_create.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_create.cpp b/src/gausskernel/cbb/bbox/bbox_create.cpp new file mode 100644 index 000000000..54ccf182d --- /dev/null +++ b/src/gausskernel/cbb/bbox/bbox_create.cpp @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * bbox_create.cpp + * + * IDENTIFICATION + * src/gausskernel/cbb/bbox/bbox_create.cpp + * + * ------------------------------------------------------------------------- + */ +#include "bbox_elf_dump_base.h" +#include "bbox_create.h" +#include "bbox.h" +#include "../../src/include/securec.h" +#include "../../src/include/securec_check.h" + +/* Core path of the bbox process */ +char g_szBboxCorePath[BBOX_NAME_PATH_LEN] = "./"; + +/* count of the BBOX process */ +s32 g_iBBoxCoreFileCount = 30; + +/* the max core file size of bbox process */ +s32 g_iBBoxCoreFileSize = 50; + +long int g_iCoreDumpBeginTime = 0; +long int g_iCoreDumpEndTime = 0; + +char g_acDateTime[BBOX_TINE_LEN]; + +/* + * get system date time. + * return 0 if sucess else err code. + */ +int BBOX_GetSysDateTime(void) +{ + int iCommandFD = -1; + int iReadSize = 0; + + // 打开一个管道,并执行指定的日期时间命令 + BBOX_NOINTR(iCommandFD = sys_popen(BBOX_DATE_TIME_CMD, "r")); + if (iCommandFD < 0) { + bbox_print(PRINT_ERR, "sys_popen is failed, errno = %d.\n", errno); + return RET_ERR; + } + + // 读取sys_read命令的结果 + BBOX_NOINTR(iReadSize = sys_read(iCommandFD, g_acDateTime, BBOX_TINE_LEN)); + if (iReadSize <= 0) { + (void)sys_pclose(iCommandFD); + bbox_print(PRINT_ERR, "sys_read is failed, iReadSize = %d.\n", iReadSize); + return RET_ERR; + } + + (void)sys_pclose(iCommandFD); + + if (iReadSize > 0) { + g_acDateTime[iReadSize - 1] = '\0'; /* 移除'\n'字符 */ + } + + bbox_print(PRINT_LOG, "Get system time %s.\n", g_acDateTime); + + return RET_OK; +} + +/* + * get default core file name + * return 0 if sucess else err code. + */ +s32 BBOX_GetDefaultCoreName(char* szFileName, s32 iSize, const char* szAddText) +{ + if (szFileName == NULL) { + return RET_ERR; + } + + if (szAddText == NULL) { + szAddText = ""; + } + + struct kernel_timeval stProgramCoreDumpTime = {0}; + sys_gettimeofday(&stProgramCoreDumpTime, NULL); + + s32 ret = bbox_snprintf(szFileName, + iSize, + "%s/core-%s-%d-%s-%s.lz4", + g_szBboxCorePath, + progname, + sys_getpid(), + g_acDateTime, + szAddText); + + return (ret > 0) ? RET_OK : RET_ERR; +} + +/* + * get temp core file name + * return 0 if sucess else err code. + */ +s32 BBOX_GetTmpCoreName(char* szFileName, s32 iSize) +{ + if (szFileName == NULL) { + return RET_ERR; + } + + struct kernel_timeval stProgramCoreDumpTime = {0}; + sys_gettimeofday(&stProgramCoreDumpTime, NULL); + + g_iCoreDumpBeginTime = stProgramCoreDumpTime.tv_sec; + bbox_print(PRINT_TIP, "coredump begin at %ld\n", stProgramCoreDumpTime.tv_sec); + + s32 ret = bbox_snprintf(szFileName, + iSize, + "%s/%s-%d-%s-%s", + g_szBboxCorePath, + progname, + sys_getpid(), + g_acDateTime, + BBOX_TMP_FILE_ADD_NAME); + + return (ret > 0) ? RET_OK : RET_ERR; +} + +/* + * get core file count of bbox process. + * return 0 if sucess else err code. + */ +s32 BBOX_GetBBoxFileCount(const char* pszPath, const char* pszName, void* pArgs) +{ + s32* pIFileCount = NULL; + + pIFileCount = (s32*)pArgs; + if (pIFileCount == NULL) { + bbox_print(PRINT_ERR, "Invalid argument pstArgs\n"); + return RET_ERR; + } + + /* ignore path "." and ".." */ + if ((0 == bbox_strcmp(pszName, ".")) || (0 == bbox_strcmp(pszName, ".."))) { + return RET_OK; + } + + /* ignore the file which not belong to bbox */ + if ((0 == bbox_strstr(pszName, BBOX_SNAP_FILE_ADD_NAME ".lz4")) && + (0 == bbox_strstr(pszName, BBOX_CORE_FILE_ADD_NAME ".lz4"))) { + return RET_OK; + } + + /* ignore the file which not created by this process. */ + if (bbox_strstr(pszName, progname) == 0) { + return RET_OK; + } + + /* file count ++ */ + *pIFileCount = *pIFileCount + 1; + + return RET_OK; +} + +/* + * get bbox ildiest name. + * return 0 if sucess else err code. + */ +s32 BBOX_GetBBoxOldiestName(const char* pszPath, const char* pszName, void* pArgs) +{ + struct BBOX_ListDirParam* pstArgs = NULL; + struct kernel_stat* pstOldiestState = NULL; + char* pszOldName = NULL; + struct kernel_stat stCurrState = {0}; + char szFileName[BBOX_NAME_PATH_LEN]; + + // 检查参数 + pstArgs = (struct BBOX_ListDirParam*)pArgs; + if (pstArgs == NULL) { + bbox_print(PRINT_ERR, "Invalid argument pstArgs\n"); + return RET_ERR; + } + + pstOldiestState = (struct kernel_stat*)pstArgs->pArg1; + pszOldName = (char*)pstArgs->pArg2; + + // 忽略路径"."和".." + if ((0 == bbox_strcmp(pszName, ".")) || (0 == bbox_strcmp(pszName, ".."))) { + return RET_OK; + } + + // 忽略不属于bbox的文件 + if ((0 == bbox_strstr(pszName, BBOX_SNAP_FILE_ADD_NAME ".lz4")) && + (0 == bbox_strstr(pszName, BBOX_CORE_FILE_ADD_NAME ".lz4"))) { + return RET_OK; + } + + // 忽略不是由该进程创建的文件 + if (bbox_strstr(pszName, progname) == 0) { + return RET_OK; + } + + // 拼接文件路径 + if (bbox_snprintf(szFileName, sizeof(szFileName), "%s/%s", pszPath, pszName) <= 0) { + bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + return RET_ERR; + } + + // 获取文件信息 + if (sys_stat(szFileName, &stCurrState) < 0) { + bbox_print(PRINT_ERR, "Get stat of '%s' failed, errno = %d\n", szFileName, errno); + return RET_ERR; + } + + // 比较并判断是否为最早创建的文件 + if (stCurrState.st_mtime_ < pstOldiestState->st_mtime_) { + // 记录最早的文件信息 + *pstOldiestState = stCurrState; + if (bbox_snprintf(pszOldName, BBOX_NAME_PATH_LEN, "%s/%s", pszPath, pszName) <= 0) { + bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + return RET_ERR; + } + } + + return RET_OK; +} + +/* + * remove the oldiest bbox file. + */ +void BBOX_RemoveOldiestBBoxFile(void) +{ + s32 iRet = 0; + struct kernel_stat stOldiestState; + struct BBOX_ListDirParam stArgs = {0}; + char szFileName[BBOX_NAME_PATH_LEN]; + errno_t rc = EOK; + + /* set the file modified time to the max value acquiescently. */ + rc = memset_s(&stOldiestState, sizeof(stOldiestState), 0xFF, sizeof(stOldiestState)); + securec_check_c(rc, "\0", "\0"); + + stOldiestState.st_size = -1; + + stArgs.pArg1 = &stOldiestState; + stArgs.pArg2 = szFileName; + + /* search the oldiest file. */ + iRet = bbox_listdir(g_szBboxCorePath, BBOX_GetBBoxOldiestName, &stArgs); + if (iRet != RET_OK) { + return; + } + + /* remove it if found. */ + if (-1 != stOldiestState.st_size) { + sys_unlink(szFileName); + } +} + +/* + * remove the old bbox file + */ +void BBOX_RemoveOldBBoxFile(void) +{ + s32 iFileCount = 0; + s32 iRet = 0; + s32 i; + + iRet = bbox_listdir(g_szBboxCorePath, BBOX_GetBBoxFileCount, &iFileCount); + if (iRet != RET_OK) { + bbox_print(PRINT_ERR, "Get bbox file count failed.\n"); + return; + } + + /* remove redundant file */ + for (i = 0; i < (iFileCount - g_iBBoxCoreFileCount + 1); i++) { + /* remove the oldiest bbox file. */ + BBOX_RemoveOldiestBBoxFile(); + } +} + +/* + * remove bbox temp file. + */ +s32 BBOX_DoRemoveTempBBoxFile(const char* pszPath, const char* pszName, void* pArgs) +{ + struct kernel_stat stCurrState = {0}; + struct kernel_timeval stCurrTime = {0}; + char szFileName[BBOX_NAME_PATH_LEN]; + + /* ignore path "." and ".." */ + if ((0 == bbox_strcmp(pszName, ".")) || (0 == bbox_strcmp(pszName, ".."))) { + return RET_OK; + } + + /* ignore the file which not belong to bbox */ + if (0 == bbox_strstr(pszName, BBOX_TMP_FILE_ADD_NAME)) { + return RET_OK; + } + + /* ignore the file which not created by this process. */ + if (0 != bbox_strncmp(pszName, progname, bbox_strlen(progname))) { + return RET_OK; + } + + if (bbox_snprintf(szFileName, sizeof(szFileName), "%s/%s", pszPath, pszName) <= 0) { + bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + return RET_ERR; + } + + if (sys_stat(szFileName, &stCurrState) < 0) { + bbox_print(PRINT_ERR, "Get stat of '%s' failed, errno = %d\n", szFileName, errno); + return RET_ERR; + } + + /* get current time */ + sys_gettimeofday(&stCurrTime, NULL); + + /* remove temp file before BBOX_TMP_DEL_TIME_INTERVAL */ + if (stCurrState.st_mtime_ < (unsigned long)(stCurrTime.tv_sec - BBOX_TMP_DEL_TIME_INTERVAL)) { + /* rm file */ + bbox_print(PRINT_ERR, "Delete bad bbox core file %s \n", szFileName); + sys_unlink(szFileName); + } + + return RET_OK; +} + +/* + * remove temp bbox file + */ +void BBOX_RemoveTempBBoxFile(void) +{ + s32 iRet = 0; + + iRet = bbox_listdir(g_szBboxCorePath, BBOX_DoRemoveTempBBoxFile, NULL); + if (iRet != RET_OK) { + bbox_print(PRINT_ERR, "Get bbox file count failed.\n"); + return; + } +} + +/* + finish core dump file. + in : args defined by user and it is file name here. + +*/ +void BBOX_FinishDumpFile(void* args) +{ + // 声明所需的变量 + char* pszNewName = NULL; + char* pszOldName = NULL; + struct kernel_timeval stProgramCoreDumpTime = {0}; + struct BBOX_ListDirParam* pstArgs = (struct BBOX_ListDirParam*)args; + + // 检查参数是否为NULL + if (args == NULL) { + bbox_print(PRINT_ERR, "BBOX_FinishDumpFile args is null.\n"); + return; + } + + // 获取程序核心转储完成的时间 + sys_gettimeofday(&stProgramCoreDumpTime, NULL); + + // 将程序核心转储完成的时间赋值给全局变量 + g_iCoreDumpEndTime = stProgramCoreDumpTime.tv_sec; + bbox_print(PRINT_TIP, "coredump End at %ld\n", stProgramCoreDumpTime.tv_sec); + + // 打印程序核心转储所用的时间 + bbox_print(PRINT_TIP, "coredump used time: %ld sec\n", g_iCoreDumpEndTime - g_iCoreDumpBeginTime); + + // 移除最旧的文件 + BBOX_RemoveOldBBoxFile(); + + // 获取新文件名和旧文件名 + pszNewName = (char*)pstArgs->pArg1; + pszOldName = (char*)pstArgs->pArg2; + + // 将旧文件的访问权限设置为0600 + if (sys_chmod(pszOldName, 0600)) { + bbox_print(PRINT_ERR, "set %s mode to 0600 failed, errno = %d\n", pszOldName, errno); + } + + // 重命名文件 + if (pszNewName != NULL && pszOldName != NULL) { + if (sys_rename(pszOldName, pszNewName) < 0) { + bbox_print(PRINT_ERR, "rename file %s to %s failed, errno = %d.\n", pszOldName, pszNewName, errno); + } + } + + // 移除临时核心转储文件 + BBOX_RemoveTempBBoxFile(); +} + +/* + * create core dump file. + * return 0 if success else err code. + */ +s32 BBOX_CreateCoredump(char* file_name) +{ + s32 iRet = 0; + char szFileName[BBOX_NAME_PATH_LEN]; + char szTmpName[BBOX_NAME_PATH_LEN]; + struct BBOX_ListDirParam stArgs = {0}; + char* file_tmp = file_name; + FRAME(frame); + + bbox_initlog(0); + + // 打印日志头 + bbox_print(PRINT_TIP, "\nBBOX LOG\n-------------------------------\n"); + + // 获取系统当前时间 + iRet = BBOX_GetSysDateTime(); + if (iRet != RET_OK) { + return RET_ERR; + } + + // 创建核心文件保存路径 + if (bbox_mkdir(g_szBboxCorePath) < 0) { + bbox_print(PRINT_ERR, "bbox_mkdir is failed, errno = %d.\n", errno); + return RET_ERR; + } + + // 若file_name为NULL,则使用默认名称创建核心文件 + if (file_name == NULL) { + if (BBOX_GetDefaultCoreName(szFileName, BBOX_NAME_PATH_LEN, BBOX_CORE_FILE_ADD_NAME) == RET_OK) { + file_name = szFileName; + } else { + bbox_print(PRINT_ERR, "BBOX_GetDefaultCoreName is failed, errno = %d.\n", errno); + return RET_ERR; + } + } + + // 打印核心文件路径 + bbox_print(PRINT_TIP, "core file path is %s\n", file_name); + + // 获取临时核心文件名 + if (BBOX_GetTmpCoreName(szTmpName, BBOX_NAME_PATH_LEN) == RET_OK) { + file_tmp = szTmpName; + } else { + bbox_print(PRINT_ERR, "BBOX_GetTmpCoreName is failed, errno = %d.\n", errno); + return RET_ERR; + } + + // 设置参数并调用相关函数 + stArgs.pArg1 = file_name; + stArgs.pArg2 = file_tmp; + + iRet = BBOX_GetAllThreads(GET_TYPE_DUMP, BBOX_FinishDumpFile, &stArgs, BBOX_DoDumpElfCore, &frame, file_tmp); + + return iRet; +} + +/* + * set core dump path + * return 0 if success else err code. + */ +s32 BBOX_SetCoredumpPath(const char* pszPath) +{ + if (NULL != pszPath) { + if (bbox_snprintf(g_szBboxCorePath, sizeof(g_szBboxCorePath), "%s", pszPath) <= 0) { + bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + return RET_ERR; + } + } else { + return RET_ERR; + } + + if (0 == bbox_strlen(g_szBboxCorePath)) { + return RET_ERR; + } + + if (bbox_mkdir(g_szBboxCorePath) < 0) { + bbox_print(PRINT_ERR, "bbox_mkdir is failed, errno = %d.\n", errno); + return RET_ERR; + } + + return RET_OK; +} + +/* + * set core file count + * return 0 if success else err code. + */ +s32 BBOX_SetCoreFileCount(s32 iCount) +{ + if (iCount < 0) { + return RET_ERR; + } + + g_iBBoxCoreFileCount = iCount; + + return RET_OK; +} + +/* + * add an blacklist item to exclude it from core file. + * void *pAddress : the head address of excluded memory + * u64 uilen : memory size + * return RET_OK if success else RET_ERR. + */ +s32 BBOX_AddBlackListAddress(void* pAddress, u64 uiLen) +{ + if (pAddress == NULL || uiLen == 0) { + bbox_print(PRINT_ERR, "parameter pAddress(******) uiLen(%llu) is invaild.\n", uiLen); + return RET_ERR; + } + + return _BBOX_AddBlackListAddress(pAddress, uiLen); +} + +/* + * remove an blacklist item. + * void *pAddress : the head address of excluded memory + * return RET_OK if success else RET_ERR. + */ +s32 BBOX_RmvBlackListAddress(void* pAddress) +{ + if (pAddress == NULL) { + bbox_print(PRINT_ERR, "parameter pAddress(******) is invaild.\n"); + return RET_ERR; + } + + return _BBOX_RmvBlackListAddress(pAddress); +} + -- 2.34.1 From 330153cd8861b016624e552e969e36366b692b36 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:16:01 +0800 Subject: [PATCH 20/56] Delete 'src/gausskernel/cbb/bbox/bbox_elf_dump.cpp' --- src/gausskernel/cbb/bbox/bbox_elf_dump.cpp | 2892 -------------------- 1 file changed, 2892 deletions(-) delete mode 100644 src/gausskernel/cbb/bbox/bbox_elf_dump.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_elf_dump.cpp b/src/gausskernel/cbb/bbox/bbox_elf_dump.cpp deleted file mode 100644 index 2862c30ca..000000000 --- a/src/gausskernel/cbb/bbox/bbox_elf_dump.cpp +++ /dev/null @@ -1,2892 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * bbox_elf_dump.cpp - * - * IDENTIFICATION - * src/gausskernel/cbb/bbox/bbox_elf_dump.cpp - * - * ------------------------------------------------------------------------- - */ -#include "bbox_elf_dump.h" -#include "bbox_syscall_support.h" -#include "postgres.h" -#include "gs_bbox.h" -#include "../../src/include/securec.h" -#include "../../src/include/securec_check.h" - -#if UINTPTR_MAX == 0xffffffff -#define Elf_Ehdr Elf32_Ehdr -#else -#define Elf_Ehdr Elf64_Ehdr -#endif - -#ifdef __cplusplus -#if __cplusplus -extern "C" { -#endif -#endif /* __cplusplus */ - -extern long int g_iCoreDumpBeginTime; /* begin time of coredump */ - -struct BBOX_ELF_SECTION g_stElfSectionInfo; /* information of all section. */ -BBOX_SECTION_STRU g_stSectionInfo[BBOX_SECTION_NUM]; /* array to record section information. */ -char g_acBboxAddonInfo[BBOX_ADDON_INFO_SIZE]; /* record system information. */ -char g_acBboxStrTabInfo[BBOX_SH_STR_TAB_SIZE]; /* record string symbol table. */ - -/* - * ignore the field that discribe equipment or node when analyze a line of /proc/self/maps. - * in : struct BBOX_READ_FILE_IO *pstReadIO - file pointer to be read - * return : iGetChar - current character of the file being read - * RET_ERR - failed - */ -static int BBOX_SkipDeviceAndNodeField(struct BBOX_READ_FILE_IO* pstReadIO) -{ - int iCount = -1; - int iGetChar = -1; - - if (NULL == pstReadIO) { - - bbox_print(PRINT_ERR, "BBOX_SkipDeviceAndNodeField parameters is invalid: pstReadIO is NULL.\n"); - - return RET_ERR; - } - - iGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == iGetChar) { - - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", iGetChar); - - return RET_ERR; - } - - for (iCount = 0; iCount < DEVICE_AND_NODE_FIELD_NUM; iCount++) { - while (iGetChar == ' ') { - iGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == iGetChar) { - - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", iGetChar); - - return RET_ERR; - } - } - - while (iGetChar != ' ' && iGetChar != '\n') { - if (RET_ERR == iGetChar) { - - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", iGetChar); - - return RET_ERR; - } - iGetChar = BBOX_GetCharFromFile(pstReadIO); - } - - while (iGetChar == ' ') { - iGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == iGetChar) { - - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", iGetChar); - - return RET_ERR; - } - } - } - - return iGetChar; -} - -/* - * set whether the mapping is labeled as PF_DEVICE, means that, whether or not it's a device mapping. - * in : int *piGetChar - pointer to the character read - * struct BBOX_READ_FILE_IO *pstReadIO - pointer to struct of file read - * struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to discription of mapping segment structure. - * return RET_OK or RET_ERR - */ -static int BBOX_SetMappingDeviceFlag( - int* piGetChar, struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) -{ - int iIsMappingDevicesFlag = BBOX_FALSE; - const char* pszDeviceZero = DEVICE_ZERO_NAME_STRING; - const char* pszDevice = pszDeviceZero; - - if (NULL == piGetChar || NULL == pstReadIO || NULL == pstSegmentMapping) { - - bbox_print(PRINT_ERR, - "BBOX_FillMappingFlagsAndOffset parameters is invalid: " \ - "piGetChar, pstReadIO or pstSegmentMapping is NULL.\n"); - - return RET_ERR; - } - - /* compare and determine if it is a device */ - while (*pszDevice && *piGetChar == *pszDevice) { - *piGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == *piGetChar) { - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, *piGetChar= %d.\n", *piGetChar); - return RET_ERR; - } - - pszDevice++; - } - - iIsMappingDevicesFlag = (pszDevice >= pszDeviceZero + DEVICE_PREFIX_LEN) && - ((*piGetChar != '\n' && *piGetChar != ' ') || *pszDevice != '\000'); - if (BBOX_TRUE == iIsMappingDevicesFlag) { - pstSegmentMapping->iFlags |= PF_DEVICE; /* set flag of equipment. */ - - bbox_print(PRINT_DBG, - " Get Device Segment: StartAddr = %zu, EndAddr = %zu.\n", - pstSegmentMapping->uiStartAddress, - pstSegmentMapping->uiEndAddress); - } - - return RET_OK; -} - -/* - * set whether the mapping is labeled as PF_VDSO, means that, whether or not it's a VDSO mapping. - * in : int *piGetChar - pointer to the character read - * struct BBOX_READ_FILE_IO *pstReadIO - pointer to struct of file read - * struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to discription of mapping segment structure. - * return RET_OK or RET_ERR - */ -static int BBOX_SetMappingVDSOFlag( - int* piGetChar, struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) -{ - int iIsMappingVdsoFlag = BBOX_FALSE; - int iIsMappingVvarFlag = BBOX_TRUE; - const char* pszVdso = VDSO_NAME_STRING; - const char* pszVvar = VVAR_NAME_STRING; - - if (NULL == piGetChar || NULL == pstReadIO || NULL == pstSegmentMapping) { - - bbox_print(PRINT_ERR, - "BBOX_FillMappingFlagsAndOffset parameters is invalid: piGetChar," \ - "pstReadIO or pstSegmentMapping is NULL.\n"); - - return RET_ERR; - } - - while (*pszVdso && *piGetChar == *pszVdso) { - if (iIsMappingVvarFlag == BBOX_TRUE) { - iIsMappingVvarFlag = (*piGetChar == *pszVvar) ? BBOX_TRUE : BBOX_FALSE; - pszVvar++; - } - *piGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == *piGetChar) { - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, *piGetChar= %d.\n", *piGetChar); - - return RET_ERR; - } - - pszVdso++; - } - - iIsMappingVdsoFlag = (*pszVdso == '\0' && (*piGetChar == '\n' || *piGetChar == ' ' || *piGetChar == '\0')); - if (BBOX_TRUE == iIsMappingVdsoFlag) { - pstSegmentMapping->iFlags |= PF_VDSO; /* set VDSO flag. */ - - bbox_print(PRINT_DBG, - " Get VDSO StartAddr = %zu, EndAddr = %zu.\n", - pstSegmentMapping->uiStartAddress, - pstSegmentMapping->uiEndAddress); - } - - if (iIsMappingVvarFlag == BBOX_TRUE) { - while (*pszVdso && *piGetChar == *pszVvar) { - *piGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == *piGetChar) { - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, *piGetChar= %d.\n", *piGetChar); - - return RET_ERR; - } - - pszVvar++; - } - - if (*pszVvar == '\0' && (*piGetChar == '\n' || *piGetChar == ' ' || *piGetChar == '\0')) { - pstSegmentMapping->iFlags |= PF_VVAR; /* set VVAR flag */ - - bbox_print(PRINT_DBG, - " Get VVAR StartAddr = %zu, EndAddr = %zu.\n", - pstSegmentMapping->uiStartAddress, pstSegmentMapping->uiEndAddress); - } - } - - return RET_OK; -} - -/* - * check if the file is a dynamic library file. - * in : char* pszFilePath - path - * struct BBOX_VM_MAPS *pstSegmentMapping - pointer to discription of mapping segment structuer. - * return RET_OK or RET_ERR. - */ -static int BBOX_SettingMappedFileFlag(char* pszFilePath, struct BBOX_VM_MAPS* pstSegmentMapping) -{ - int fd = -1; - int retval = -1; - Elf_Ehdr ehdr; - - fd = sys_open(pszFilePath, O_RDONLY, 0); - if (fd < 0) { - bbox_print(PRINT_ERR, "open file %s failed, errno = %d\n", pszFilePath, errno); - return RET_ERR; - } - - /* read ELF header */ - retval = sys_read(fd, &ehdr, sizeof(ehdr)); - if (retval < 0) { - bbox_print(PRINT_ERR, "read elf failed, errno = %d\n", errno); - goto errout; - } - - /* check if the file is ELF file. */ - if (bbox_strncmp(ELFMAG, (char*)ehdr.e_ident, SELFMAG)) { - pstSegmentMapping->iFlags |= PF_MAPPEDFILE; - bbox_print(PRINT_DBG, "not an elf file.\n"); - /* this is map file instead of dynamic library file. */ - bbox_print(PRINT_DBG, "mapped file : %s\n", pszFilePath); - } - - sys_close(fd); - return RET_OK; - -errout: - - if (fd > 0) { - sys_close(fd); - } - return RET_ERR; -} - -/* - * check if the file is equipment file. - * in : char* pszFilePath - path - * struct BBOX_VM_MAPS *pstSegmentMapping - pointer to discription of mapping segment structuer. - * return RET_OK or RET_ERR. - */ -static int BBOX_SettingDeviceFileFlag(char* pszFilePath, struct BBOX_VM_MAPS* pstSegmentMapping) -{ - struct kernel_stat stMarkerSB = {0}; - if (sys_stat(pszFilePath, &stMarkerSB) < 0) { - bbox_print(PRINT_ERR, "sys_stat error, errno = %d, path = %s\n", errno, pszFilePath); - return RET_ERR; - } - - if (S_ISCHR(stMarkerSB.st_mode) || S_ISBLK(stMarkerSB.st_mode) || S_ISFIFO(stMarkerSB.st_mode) || - S_ISSOCK(stMarkerSB.st_mode)) { - bbox_print(PRINT_DBG, "Device file : %s\n", pszFilePath); - pstSegmentMapping->iFlags |= PF_DEVICE; - return RET_OK; - } - - return RET_ERR; -} - -/* - * check if corresponding address is a general file mapping by mmap instead of a dynamic library file - * when read /proc/self/maps. - * in : struct BBOX_READ_FILE_IO *pstReadIO - pointer to file to be read. - * struct BBOX_VM_MAPS *pstSegmentMapping - pointer to discription of mapping segment structuer. - * return : int iGetChar - current character of file reading - * RET_ERR - read failed - */ -static int BBOX_SettingFileFlags( - int* iGetChar, struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) -{ - char MapFilePath[PATH_MAX]; - int i = 0; - - MapFilePath[0] = *iGetChar; - i++; - - /* read mapping file path after reading all address. */ - while ((*iGetChar = BBOX_GetCharFromFile(pstReadIO)) != '\n' && i < PATH_MAX - 1) { - if (RET_ERR == *iGetChar) { - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", *iGetChar); - return RET_ERR; - } - - MapFilePath[i] = *iGetChar; - i++; - } - - MapFilePath[i] = 0; - - if (BBOX_SettingDeviceFileFlag(MapFilePath, pstSegmentMapping) == RET_OK) { - return RET_OK; - } - - if (BBOX_SettingMappedFileFlag(MapFilePath, pstSegmentMapping) == RET_OK) { - return RET_OK; - } - - return RET_ERR; -} - -/* - * fill flag and offset of struct BBOX_VM_MAPS when reading /proc/self/maps. - * in : struct BBOX_READ_FILE_IO *pstReadIO - pointer to file to be read. - * struct BBOX_VM_MAPS *pstSegmentMapping - pointer to discription of mapping segment structuer. - * return : int iGetChar - current character of file reading - * RET_ERR - read failed - */ -static int BBOX_FillMappingFlagsAndOffset(struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) -{ - int iRessult = 0; - int iGetChar = -1; - int iIsMappingAnonymousFlag = BBOX_FALSE; - int iIsMappedFile = BBOX_FALSE; - - if (NULL == pstReadIO || NULL == pstSegmentMapping) { - bbox_print(PRINT_ERR, - "BBOX_FillMappingFlagsAndOffset parameters is invalid: pstReadIO or " \ - "pstSegmentMapping is NULL.\n"); - return RET_ERR; - } - - /* read flags and set '-' to 0 after reading all address. */ - while ((iGetChar = BBOX_GetCharFromFile(pstReadIO)) != ' ') { - if (RET_ERR == iGetChar) { - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", iGetChar); - return RET_ERR; - } - - pstSegmentMapping->iFlags = (pstSegmentMapping->iFlags << 1) | (unsigned int)(iGetChar != '-'); - } - - pstSegmentMapping->iFlags = ((pstSegmentMapping->iFlags) >> 1) & PF_MASK; - - /* read offset */ - iGetChar = BBOX_StringSwitchInt(pstReadIO, &(pstSegmentMapping->uiOffset)); - if (RET_ERR == iGetChar) { - bbox_print(PRINT_ERR, "BBOX_StringSwitchInt is failed, iGetChar= %d.\n", iGetChar); - return RET_ERR; - } - - /* ignore the feild that discribe equipment and node which are not needed. */ - iGetChar = BBOX_SkipDeviceAndNodeField(pstReadIO); - if (RET_ERR == iGetChar) { - bbox_print(PRINT_ERR, "BBOX_SkipDeviceAndNodeField is failed, iGetChar= %d.\n", iGetChar); - return RET_ERR; - } - - /* judge the next field is start with '[' or is end, if yes, mark it as a anonymity equipment. */ - iIsMappingAnonymousFlag = ((iGetChar == '\n') || (iGetChar == '[')); - if (BBOX_TRUE == iIsMappingAnonymousFlag) { - pstSegmentMapping->iFlags |= PF_ANONYMOUS; - /* judge where it is VDSO segment, if yes, mark it. */ - iRessult = BBOX_SetMappingVDSOFlag(&iGetChar, pstReadIO, pstSegmentMapping); - if (RET_OK != iRessult) { - bbox_print(PRINT_ERR, "BBOX_SetMappingVDSOFlag is failed, iRessult= %d.\n", iRessult); - return RET_ERR; - } - - return iGetChar; - } - - /* judge if it is discribing someone equipment. */ - iRessult = BBOX_SetMappingDeviceFlag(&iGetChar, pstReadIO, pstSegmentMapping); - if (RET_OK != iRessult) { - bbox_print(PRINT_ERR, "BBOX_SetMappingDeviceFlag is failed, iRessult= %d.\n", iRessult); - return RET_ERR; - } - - /* judge if it has mapping file. */ - iIsMappedFile = (iGetChar == '/'); - - if (BBOX_TRUE == iIsMappedFile) { - iRessult = BBOX_SettingFileFlags(&iGetChar, pstReadIO, pstSegmentMapping); - if (RET_OK != iRessult) { - bbox_print(PRINT_ERR, "BBOX_SettingFileFlags is failed, iRessult= %d.\n", iRessult); - return RET_ERR; - } - } - - return iGetChar; -} - -/* - * read /proc/self/maps and get count of line in which. - * return : iLineNum - line count of /proc/self/maps, it is not a negative. - * RET_ERR - */ -static int BBOX_GetVmMapsNum(void) -{ - int iFd = -1; - int iLineNum = 0; - int iCount = 0; - ssize_t iReadSize = -1; - char acBuff[BBOX_BUFF_LITTLE_SIZE]; - errno_t rc = EOK; - BBOX_NOINTR(iFd = sys_open(THREAD_SELF_MAPS_FILE, O_RDONLY, 0)); - if (iFd < 0) { - bbox_print(PRINT_ERR, "sys_open is failed, iFd= %d.\n", iFd); - return RET_ERR; - } - - bbox_print(PRINT_DBG, "Read file : /proc/self/Maps:\n"); - - do { - iReadSize = -1; - rc = memset_s(acBuff, BBOX_BUFF_LITTLE_SIZE, 0, sizeof(acBuff)); - securec_check_c(rc, "\0", "\0"); - - BBOX_NOINTR(iReadSize = sys_read(iFd, acBuff, sizeof(acBuff))); - if (RET_ERR == iReadSize) { - BBOX_NOINTR(sys_close(iFd)); - return RET_ERR; - } else if (0 == iReadSize) { - break; - } - - bbox_print(PRINT_DBG, "%s", acBuff); - - for (iCount = 0; iCount < iReadSize; iCount++) { - if ('\n' == acBuff[iCount]) { - iLineNum++; - } - } - } while (iReadSize); - - BBOX_NOINTR(sys_close(iFd)); - - return iLineNum ? iLineNum : RET_ERR; -} - -/* - * fill address feild in struct BBOX_VM_MAPS when reading /proc/self/maps. - * in : struct BBOX_READ_FILE_IO *pstReadIO - pointer to file to be read. - * struct BBOX_VM_MAPS *pstSegmentMapping - pointer to discription of mapping segment structuer. - * return : int iGetChar - current character of file reading - * RET_ERR - read failed - */ -static char BBOX_FillMappingAddress(struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) -{ - int iGetChar = -1; - - if (NULL == pstReadIO || NULL == pstSegmentMapping) { - bbox_print(PRINT_ERR, - "BBOX_FillMappingAddress parameters is invalid: pstReadIO or pstSegmentMapping is NULL.\n"); - return RET_ERR; - } - - /* read characters from file and convert it to interger until get '-'. - This is start address and then ready to read end address. */ - iGetChar = BBOX_StringSwitchInt(pstReadIO, &(pstSegmentMapping->uiStartAddress)); - if ('-' == iGetChar) { - /* read characters from file and convert it to interger until get '-'. This is end address. */ - iGetChar = BBOX_StringSwitchInt(pstReadIO, &(pstSegmentMapping->uiEndAddress)); - if (' ' != iGetChar) { - bbox_print(PRINT_ERR, "BBOX_StringSwitchInt is failed, iGetChar= %d.\n", iGetChar); - - return RET_ERR; - } - } else { - - bbox_print(PRINT_ERR, "BBOX_StringSwitchInt is failed, iGetChar= %d.\n", iGetChar); - - return RET_ERR; - } - - return (char)iGetChar; -} - -/* - * use blacklist items to split the segment, thus drop partal slice from core file. - * struct BBOX_WRITE_FDS *pstWriteFds : segment to be written into core file - * return RET_OK if success else RET_ERR. - */ -static int BBOX_VmExecludeBlackList(struct BBOX_VM_MAPS *pstVmMappingSegment) -{ - void *pStartAddress = (void *)(uintptr_t)pstVmMappingSegment->uiStartAddress; - void *pEndAddress = (void *)(uintptr_t)pstVmMappingSegment->uiEndAddress; - void *pBlackStartAddress = NULL; - void *pBlackEndAddress = NULL; - size_t uiPageSize = sys_sysconf(_SC_PAGESIZE); - BBOX_BLACKLIST_STRU *pstBlackNode = NULL; - struct BBOX_VM_MAPS *pstNextVmSegment = NULL; - - pstBlackNode = _BBOX_FindAddrInBlackList(pStartAddress, pEndAddress); - if (pstBlackNode != NULL) { - - pBlackStartAddress = pstBlackNode->pBlackStartAddr; - pBlackEndAddress = pstBlackNode->pBlackEndAddr; - - pstNextVmSegment = pstVmMappingSegment + 1; - pstNextVmSegment->iFlags = pstVmMappingSegment->iFlags; - - if (pStartAddress < pBlackStartAddress) { - pstVmMappingSegment->uiWriteSize= (size_t)((uintptr_t)pBlackStartAddress - (uintptr_t)pStartAddress); - if ((pstVmMappingSegment->uiWriteSize) < uiPageSize) { - pstVmMappingSegment->uiEndAddress = - (size_t)((uintptr_t)pBlackStartAddress + (pstVmMappingSegment->uiWriteSize % uiPageSize)); - } else { - pstVmMappingSegment->uiEndAddress = (size_t)(uintptr_t)pBlackStartAddress; - } - - pstVmMappingSegment->uiWriteSize= (size_t)((uintptr_t)pBlackStartAddress - (uintptr_t)pStartAddress); - pstVmMappingSegment->uiStartAddress = (size_t)(uintptr_t)pStartAddress; - pstVmMappingSegment->iIsRemoveFlags = BBOX_FALSE; - } else { - pstVmMappingSegment->iIsRemoveFlags = BBOX_TRUE; - } - - if (pBlackEndAddress < pEndAddress) { - pstNextVmSegment->uiWriteSize = (size_t)((uintptr_t)pEndAddress - (uintptr_t)pBlackEndAddress); - if ((pstNextVmSegment->uiWriteSize) < uiPageSize) { - pstNextVmSegment->uiStartAddress = - (size_t)((uintptr_t)pBlackEndAddress - (pstNextVmSegment->uiWriteSize % uiPageSize)); - } else { - pstNextVmSegment->uiStartAddress = (size_t)(uintptr_t)pBlackEndAddress; - } - - pstNextVmSegment->uiWriteSize = (size_t)((uintptr_t)pEndAddress - (uintptr_t)pBlackEndAddress); - pstNextVmSegment->uiEndAddress = (size_t)(uintptr_t)pEndAddress; - pstNextVmSegment->iIsRemoveFlags = BBOX_FALSE; - } else { - pstNextVmSegment->iIsRemoveFlags = BBOX_TRUE; - } - } else { - /* do nothing */ - } - - bbox_print(PRINT_DBG, "execlude black list success.\n"); - return RET_OK; -} - -/* - * read /proc/self/maps and fill struct BBOX_VM_MAPS - * in : struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to discription of mapping segment structure. - * int iSegmentNum - count of mapping segment in address space. - * return RET_OK or RET_ERR. - */ -static int BBOX_GetMappingSegment(struct BBOX_VM_MAPS* pstSegmentMapping, int* piSegmentNum) -{ - int iGetChar = -1; - int iResult = RET_ERR; - int iNewVmTotal = 0; - int iVmTotal = 0; - struct BBOX_READ_FILE_IO stReadIO; - struct BBOX_VM_MAPS* pstCurSegment = NULL; - errno_t rc = EOK; - if (NULL == pstSegmentMapping || NULL == piSegmentNum) { - bbox_print(PRINT_ERR, - "BBOX_GetMappingSegment parameters is invalid: " \ - "pstSegmentMapping or piSegmentNum is NULL.\n"); - return RET_ERR; - } - - iVmTotal = *piSegmentNum; - - rc = memset_s(&stReadIO, sizeof(struct BBOX_READ_FILE_IO), 0, sizeof(struct BBOX_READ_FILE_IO)); - securec_check_c(rc, "\0", "\0"); - - stReadIO.iFd = -1; - stReadIO.pData = NULL; - stReadIO.pEnd = NULL; - - BBOX_NOINTR(stReadIO.iFd = sys_open(THREAD_SELF_MAPS_FILE, O_RDONLY, 0)); - if (stReadIO.iFd < 0) { - bbox_print(PRINT_ERR, "sys_open is failed, stReadIO.iFd = %d.\n", stReadIO.iFd); - return RET_ERR; - } - - pstCurSegment = pstSegmentMapping; - while (iVmTotal) { - bbox_print(PRINT_DBG, "iVmTotal = %d.\n", iVmTotal); - - /* get start address and end address of every segment in process address space. */ - iGetChar = BBOX_FillMappingAddress(&stReadIO, pstCurSegment); - if (RET_ERR == iGetChar) { - BBOX_NOINTR(sys_close(stReadIO.iFd)); - bbox_print(PRINT_ERR, "BBOX_FillMappingAddress is failed, iGetChar = %d.\n", iGetChar); - return RET_ERR; - } - - /* get jurisdiction flag and offset of every segment in process address space. */ - iGetChar = BBOX_FillMappingFlagsAndOffset(&stReadIO, pstCurSegment); - if (RET_ERR == iGetChar) { - BBOX_NOINTR(sys_close(stReadIO.iFd)); - bbox_print(PRINT_ERR, "BBOX_FillMappingFlagsAndOffset is failed, iGetChar = %d.\n", iGetChar); - return RET_ERR; - } - - if (iGetChar != '\n') { - /* ignore other information and skip to the end of line for preparing to read next line. */ - iResult = BBOX_SkipToLineEnd(&stReadIO); - if (RET_OK != iResult) { - BBOX_NOINTR(sys_close(stReadIO.iFd)); - bbox_print(PRINT_ERR, "BBOX_SkipToLineEnd is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - } - - while (_BBOX_FindAddrInBlackList( - (void *)(uintptr_t)pstCurSegment->uiStartAddress, (void *)(uintptr_t)pstCurSegment->uiEndAddress)) { - - iResult = BBOX_VmExecludeBlackList(pstCurSegment); - if (RET_OK != iResult) { - BBOX_NOINTR(sys_close(stReadIO.iFd)); - bbox_print(PRINT_ERR, "BBOX_VmExecludeBlackList is failed.\n"); - return RET_ERR; - } - - iNewVmTotal++; - pstCurSegment++; - } - - pstCurSegment++; - iVmTotal--; - } - - (*piSegmentNum) = (*piSegmentNum) + iNewVmTotal; - - bbox_print(PRINT_TIP, "after BL, all segment num is %d.\n", *piSegmentNum); - - BBOX_NOINTR(sys_close(stReadIO.iFd)); - - return RET_OK; -} - -/* - * judge if it should be written into core file for every segment in address space, - * and calculate the size to be written in. - * in : struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to mapping segment structure. - * int iSegmentNum - count of mapping segment in address space - * int *piValidSegmentNum - count of valid mapping segment in address space, - * which need to be written into core file. - * return RET_OK or RET_ERR. - */ -static int BBOX_VmMappingSizeDump(struct BBOX_VM_MAPS* pstVmMappingSegment, int iSegmentNum, int* piValidSegmentNum) -{ - int iCount = 0; - struct BBOX_VM_MAPS* pstVmMapping = NULL; - - if (NULL == pstVmMappingSegment || NULL == piValidSegmentNum || iSegmentNum <= 0) { - bbox_print(PRINT_ERR, - "BBOX_VmMappingSizeDump parameters is invalid: pstVmMappingSegment, " \ - "piValidSegmentNum or iSegmentNum is NULL.\n"); - - return RET_ERR; - } - - *piValidSegmentNum = iSegmentNum; - - bbox_print(PRINT_LOG, "write segment to core file:\n"); - - for (iCount = 0; iCount < iSegmentNum; iCount++) { - pstVmMapping = pstVmMappingSegment + iCount; - - if (BBOX_TRUE == pstVmMapping->iIsRemoveFlags) { - pstVmMapping->uiWriteSize = 0; - (*piValidSegmentNum)--; - continue; - } - - /* If the segment is a code segment or a non-anonymous segment that cannot be read or written, - set the size written to the core file to 0, otherwise the size is calculated */ -#if defined(__ARM_ARCH_5TE__) || (defined(__ARM_ARCH_7A__)) || (defined(__aarch64__)) - if ((pstVmMapping->iFlags & (PF_ANONYMOUS | PF_W | PF_R)) == 0 || (pstVmMapping->iFlags & PF_MAPPEDFILE)) { -#else - if ((pstVmMapping->iFlags & (PF_ANONYMOUS | PF_W | PF_R)) == 0 || (pstVmMapping->iFlags & PF_X) || - (pstVmMapping->iFlags & PF_MAPPEDFILE)) { -#endif - pstVmMapping->uiWriteSize = - ((pstVmMapping->iFlags & PF_VDSO) ? (pstVmMapping->uiEndAddress - pstVmMapping->uiStartAddress) : 0); - - } else { - pstVmMapping->uiWriteSize = pstVmMapping->uiEndAddress - pstVmMapping->uiStartAddress; - - bbox_print(PRINT_DBG, "Segment[%d]: set writesize = %zu.\n", iCount, pstVmMapping->uiWriteSize); - } - - /* mark a segment not write into core file - if it cannot be read, discribes equipment segment or segment size is 0 or vvar segement */ - if (((pstVmMapping->iFlags & PF_R) == 0) || pstVmMapping->uiStartAddress == pstVmMapping->uiEndAddress || - (pstVmMapping->iFlags & PF_VVAR) || - (pstVmMapping->iFlags & PF_DEVICE)) { - pstVmMapping->uiWriteSize = 0; - (*piValidSegmentNum)--; - pstVmMapping->iIsRemoveFlags = BBOX_TRUE; - } - - if (pstVmMapping->iIsRemoveFlags != BBOX_TRUE) { - bbox_print(PRINT_DBG, - "segment[%d]: %zu %zu %u\n", - iCount, - pstVmMapping->uiOffset, - pstVmMapping->uiWriteSize, - pstVmMapping->iFlags); - } - } - - return RET_OK; -} - -/* - * fill the structure, which should be written into core file and discribes mapping segment of process address space. - * in : struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to mapping segment structure. - * int iSegmentNum - count of mapping segment in address space - * int *piValidSegmentNum - count of valid mapping segment in address space, - * which need to be written into core file. - * return RET_OK or RET_ERR. - */ -static int BBOX_FillVmMappingInfo(struct BBOX_VM_MAPS* pstVmMappingSegment, int* piSegmentNum, int* piValidSegmentNum) -{ - int iResult = RET_ERR; - - if (NULL == pstVmMappingSegment || NULL == piValidSegmentNum || NULL == piSegmentNum) { - bbox_print(PRINT_ERR, - "BBOX_FillVmMappingInfo parameters is invalid: pstVmMappingSegment, " \ - "piValidSegmentNum or piSegmentNum is NULL.\n"); - return RET_ERR; - } - - /* read /proc/self/maps and fill struct BBOX_VM_MAPS. */ - iResult = BBOX_GetMappingSegment(pstVmMappingSegment, piSegmentNum); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_GetMappingSegment is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - - /* judge if it need to be written into core file and the size to be written in - for every segment in process address space. */ - iResult = BBOX_VmMappingSizeDump(pstVmMappingSegment, *piSegmentNum, piValidSegmentNum); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_VmMappingSizeDump is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - - bbox_print(PRINT_LOG, "Fill Vm mapping segment info success.\n"); - return RET_OK; -} - -/* - * read /proc/self/auxv and get count of auxv and VDSO address - * in : union BBOX_VM_VDSO *pstVmVDSO - pointer to structure that decribes VDSO segment. - * return : iAuxvNum - count of auxv, it is not a negative. - * RET_ERR - */ -static int BBOX_GetVDSOAndVmAuxvNum(union BBOX_VM_VDSO* pstVmVDSO) -{ - int iAuxvFd = -1; - ssize_t iReadSize = RET_ERR; - int iAuxvNum = 0; - BBOX_AUXV_T stAuxv; - errno_t rc = EOK; - if (NULL == pstVmVDSO) { - bbox_print(PRINT_ERR, "BBOX_GetVDSOAndVmAuxvNum parameters is invalid: pstVmVDSO is NULL.\n"); - return RET_ERR; - } - - pstVmVDSO->pVDSOEhdr = NULL; - - /* read /proc/self/auxv, get count of Auxv written into core file and load address of VDSO. */ - BBOX_NOINTR(iAuxvFd = sys_open(THREAD_SELF_AUXV_FILE, O_RDONLY, 0)); - if (iAuxvFd < 0) { - bbox_print(PRINT_ERR, "sys_open is failed, iAuxvFd = %d.\n", iAuxvFd); - return RET_ERR; - } - - do { - iReadSize = RET_ERR; - rc = memset_s(&stAuxv, sizeof(BBOX_AUXV_T), 0, sizeof(BBOX_AUXV_T)); - securec_check_c(rc, "\0", "\0"); - - BBOX_NOINTR(iReadSize = sys_read(iAuxvFd, &stAuxv, sizeof(BBOX_AUXV_T))); - if (RET_ERR == iReadSize) { - bbox_print(PRINT_ERR, "sys_read is failed, iReadSize = %zd.\n", iReadSize); - BBOX_NOINTR(sys_close(iAuxvFd)); - return RET_ERR; - } - if (iReadSize != sizeof(BBOX_AUXV_T)) { - break; - } - - iAuxvNum++; - if (stAuxv.a_type == AT_SYSINFO_EHDR) { - /* get VDSO load address from AT_SYSINFO_EHDR of Auxv. */ - pstVmVDSO->pVDSOEhdr = (BBOX_EHDR*)stAuxv.a_un.a_val; - } - } while (stAuxv.a_type != AT_NULL); - - BBOX_NOINTR(sys_close(iAuxvFd)); - - return iAuxvNum; -} - -/* - * judge if the VDSO address we got is valid - * in : BBOX_EHDR *pstVDSOEhdr - pointer to elf header of VDSO. - * size_t uiStartAddress - start address of a segment in address space. - * size_t uiEndAddress - end address of a segment in address space. - * return : RET_OK - success - * RET_BBOX_VDSO_INVALID - failed - */ -static int BBOX_CheakVDSOEhdr(BBOX_EHDR* pstVDSOEhdr, size_t uiStartAddress, size_t uiEndAddress) -{ - int iCount = 0; - BBOX_PHDR* pstVDSOPhdr = NULL; - - if (NULL == pstVDSOEhdr) { - bbox_print(PRINT_ERR, "BBOX_CheakVDSOEhdr parameters is invalid: pstVDSOEhdr is NULL.\n"); - - return RET_ERR; - } - - const size_t uiEhdrAddress = (size_t)pstVDSOEhdr; - - if (uiEhdrAddress & (sizeof(size_t) - 1)) { - /* not aligned properly */ - bbox_print(PRINT_ERR, "VDSO is invalid: not aligned properly.\n"); - return RET_BBOX_VDSO_INVALID; - } - - if (uiEndAddress <= uiEhdrAddress + sizeof(BBOX_EHDR)) { - /* pstVDSOEhdr has Incomplete head */ - bbox_print(PRINT_ERR, "VDSO head is invalid: pstVDSOEhdr has Incomplete head.\n"); - return RET_BBOX_VDSO_INVALID; - } - - if (pstVDSOEhdr->e_phoff & (sizeof(size_t) - 1)) { - /* not aligned properly */ - bbox_print(PRINT_ERR, "VDSO Ehdr is invalid: not aligned properly.\n"); - return RET_BBOX_VDSO_INVALID; - } - - pstVDSOPhdr = (BBOX_PHDR*)(uiEhdrAddress + pstVDSOEhdr->e_phoff); - if ((size_t)pstVDSOPhdr <= uiStartAddress || uiEndAddress <= (size_t)(pstVDSOPhdr + pstVDSOEhdr->e_phnum)) { - /* VDSOPhdr is incompleted */ - bbox_print(PRINT_ERR, "VDSO Phdr is invalid: VDSOPhdr is incompleted.\n"); - return RET_BBOX_VDSO_INVALID; - } - - if (pstVDSOPhdr[0].p_type != PT_LOAD || pstVDSOPhdr[0].p_vaddr != uiStartAddress || - pstVDSOPhdr[0].p_vaddr + pstVDSOPhdr[0].p_memsz >= uiEndAddress) { - bbox_print(PRINT_ERR, "VDSO Phdr is invalid.\n"); - return RET_BBOX_VDSO_INVALID; - } - - for (iCount = 1; iCount < pstVDSOEhdr->e_phnum; iCount++) { - /* VDSO has multiple PT_LOAD segments. */ - if (pstVDSOPhdr[iCount].p_type == PT_LOAD) { - bbox_print(PRINT_ERR, "VDSO Phdr is invalid: VDSO has multiple PT_LOAD segments.\n"); - return RET_BBOX_VDSO_INVALID; - } - - /* VDSOPhdr is not aligned properly. */ - if (pstVDSOPhdr[0].p_vaddr & (sizeof(size_t) - 1)) { - bbox_print(PRINT_ERR, "VDSO Phdr is invalid: VDSOPhdr is not aligned properly.\n"); - return RET_BBOX_VDSO_INVALID; - } - - /* Phdr data range is out of bounds */ - if (pstVDSOPhdr[iCount].p_vaddr != uiStartAddress || - (pstVDSOPhdr[iCount].p_vaddr + pstVDSOPhdr[iCount].p_memsz >= uiEndAddress)) { - bbox_print(PRINT_ERR, "VDSO Phdr is invalid: Phdr data range is out of bounds.\n"); - return RET_BBOX_VDSO_INVALID; - } - } - - return RET_OK; -} - -/* - * judge if the VDSO address we got is valid and if it need to be written into core file. - * in : struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to mapping segment structure. - * int iSegmentNum - count of mapping segment in address space. - * union BBOX_VM_VDSO *pstVmVDSO - pointer to VDSO structure. - * return RET_OK or RET_ERR - */ -static int BBOX_CheckVDSO(struct BBOX_VM_MAPS* pstVmMappingSegment, int iSegmentNum, union BBOX_VM_VDSO* pstVmVDSO) -{ - int iCount = 0; - int iResult = RET_ERR; - struct BBOX_VM_MAPS* pstVmMappingTemp = NULL; - - if (NULL == pstVmMappingSegment || NULL == pstVmVDSO || iSegmentNum <= 0) { - bbox_print(PRINT_ERR, - "BBOX_CheckVDSO parameters is invalid: pstVmMappingSegment or "\ - "pstVmVDSO is NULL, iSegmentNum = %d.\n", - iSegmentNum); - - return RET_ERR; - } - - for (iCount = 0; iCount < iSegmentNum; iCount++) { - pstVmMappingTemp = (pstVmMappingSegment + iCount); - if (BBOX_FALSE == pstVmMappingTemp->iIsRemoveFlags) { - /* traverse segment, and judge if VDSO address is in segment scope. */ - if ((pstVmMappingTemp->iFlags & PF_R) && (pstVmMappingTemp->uiStartAddress <= pstVmVDSO->uiVDSOAddress) && - (pstVmMappingTemp->uiEndAddress > pstVmVDSO->uiVDSOAddress)) { - /* judge if VDSO address is valid. */ - iResult = BBOX_CheakVDSOEhdr( - pstVmVDSO->pVDSOEhdr, pstVmMappingTemp->uiStartAddress, pstVmMappingTemp->uiEndAddress); - if (RET_BBOX_VDSO_INVALID == iResult) { - pstVmVDSO->pVDSOEhdr = NULL; - } else if (RET_ERR == iResult) { - bbox_print(PRINT_ERR, "BBOX_CheakVDSOEhdr is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - break; - } - } - } - - if (iCount == iSegmentNum) { - pstVmVDSO->uiVDSOAddress = 0; - } - - return RET_OK; -} - -/* - * get count of VDSO writen into core file. - * in : union BBOX_VM_VDSO *pstVmVDSO pointer to VDSO union structure. - * return : iExtraPhdrNum - result - * RET_ERR - failed - */ -static int BBOX_GetExtraPhdrNum(union BBOX_VM_VDSO* pstVmVDSO) -{ - int iExtraPhdrNum = 0; - int iCount = 0; - BBOX_PHDR* pVDSOPhdr = NULL; - - if (NULL == pstVmVDSO) { - bbox_print(PRINT_ERR, "BBOX_GetExtraPhdrNum parameters is invalid: pstVmVDSO is NULL.\n"); - - return RET_ERR; - } else { - if (0 == pstVmVDSO->uiVDSOAddress) { - return iExtraPhdrNum; - } - - /* if segment in VDSO is not PT_LOAD, add it into core file. */ - pVDSOPhdr = (BBOX_PHDR*)(pstVmVDSO->uiVDSOAddress + pstVmVDSO->pVDSOEhdr->e_phoff); - for (iCount = 0; iCount < pstVmVDSO->pVDSOEhdr->e_phnum; iCount++) { - if (pVDSOPhdr[iCount].p_type != PT_LOAD) { - iExtraPhdrNum++; - } - } - } - - bbox_print(PRINT_DBG, "Extra Phdr Num: iExtraPhdrNum = %d.\n", iExtraPhdrNum); - - return iExtraPhdrNum; -} - -/* - * fill structure that discribe VDSO - * in : int *piAuxvNum - count of Auxv - * int *piExtraPhdrNum - count of VDSO written into core file - * int iSegmentNum - count of mapping segment in address space - * union BBOX_VM_VDSO *pstVmVDSO - pointer to structure that discribe VDSO segment. - * struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to structure that discribes mapping segment. - * return RET_OK or RET_ERR. - */ -static int BBOX_FillVDSOInfo(int* piAuxvNum, int* piExtraPhdrNum, int iSegmentNum, union BBOX_VM_VDSO* pstVmVDSO, - struct BBOX_VM_MAPS* pstVmMappingSegment) -{ - int iResult = RET_ERR; - - if (NULL == piAuxvNum || NULL == piExtraPhdrNum || NULL == pstVmVDSO || NULL == pstVmMappingSegment || - iSegmentNum <= 0) { - bbox_print(PRINT_ERR, - "BBOX_FillVDSOInfo parameters is invalid: piAuxvNum, piExtraPhdrNum," \ - "pstVmVDSO or pstVmMappingSegment is NULL, iSegmentNum = %d.\n", - iSegmentNum); - - return RET_ERR; - } - - /* get VDSO, load address of VDSO segment and count of Auxv be written into core file. */ - iResult = BBOX_GetVDSOAndVmAuxvNum(pstVmVDSO); - if (RET_ERR == iResult) { - bbox_print(PRINT_ERR, "BBOX_GetVDSOAndVmAuxvNum is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - *piAuxvNum = iResult; - - /* check if VDSO is valid */ - iResult = BBOX_CheckVDSO(pstVmMappingSegment, iSegmentNum, pstVmVDSO); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_CheckVDSO is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - /* get the number of additional segments, which means that add VDSO into segment. */ - iResult = BBOX_GetExtraPhdrNum(pstVmVDSO); - if (RET_ERR == iResult) { - bbox_print(PRINT_ERR, "BBOX_GetExtraPhdrNum is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - (*piExtraPhdrNum) += iResult; - - bbox_print(PRINT_LOG, "Fill VDSO info success.\n"); - return RET_OK; -} - -/* - * get the process run time and write it into structure. - * in : char *pszReadFile - store the character array read from file. - * int iReadSize - count of character in array. - * struct BBOX_ELF_PRPSSTATUS *pstPrPsStatus - pointer to structure that store process status information. - * return RET_OK or RET_ERR. - */ -static int BBOX_SetPsStatusTime(char* pszReadFile, int iReadSize, struct BBOX_ELF_PRPSSTATUS* pstPrPsStatus) -{ - int iFlag = 1; - const int iUTimePos = 13; /* the 13th is User time */ - const int iSTimePos = 14; /* the 14th is System time */ - const int iCUTimePos = 15; /* the 15th is Cumulative user time */ - const int iCSTimePos = 16; /* the 16th is Cumulative system time */ - const int iPendingSigPos = 30; /* the 30th is Pending signals */ - const int iHeldSigPos = 31; /* the 31th is Held signals */ - unsigned int uCount = 0; - unsigned int uItemNum = 0; - char* pcSignalstr = 0; - char* pStatItem[BBOX_STAT_ITEM_NUM]; - - if (NULL == pszReadFile || NULL == pstPrPsStatus || iReadSize <= 0) { - bbox_print(PRINT_ERR, - "BBOX_SetPsStatusTime parameters is invalid: pszReadFile or pstPrPsStatus is NULL, iReadSize = %d.\n", - iReadSize); - - return RET_ERR; - } - - for (uCount = 0; uCount < (unsigned int)iReadSize; uCount++) { - /* use pointer to record the string be divided. */ - if (iFlag) { - pStatItem[uItemNum++] = (pszReadFile + uCount); - iFlag = 0; - } - - /* convert ' ' to '\0' */ - if (pszReadFile[uCount] == ' ') { - pszReadFile[uCount] = '\0'; - iFlag = 1; - } - } - - /* the 13th is User time */ - bbox_print(PRINT_DBG, "User Time : %s\n", pStatItem[iUTimePos]); - (void)BBOX_StringToTime(pStatItem[iUTimePos], &(pstPrPsStatus->stUserTime)); - - /* the 14th is System time */ - bbox_print(PRINT_DBG, "System Time : %s\n", pStatItem[iSTimePos]); - (void)BBOX_StringToTime(pStatItem[iSTimePos], &(pstPrPsStatus->stSystemTime)); - - /* the 15th is Cumulative user time */ - bbox_print(PRINT_DBG, "Cumulative user Time : %s\n", pStatItem[iCUTimePos]); - (void)BBOX_StringToTime(pStatItem[iCUTimePos], &(pstPrPsStatus->stCumulativeUserTime)); - - /* the 16th is Cumulative system time */ - bbox_print(PRINT_DBG, "Cumulative system : %s\n", pStatItem[iCSTimePos]); - (void)BBOX_StringToTime(pStatItem[iCSTimePos], &(pstPrPsStatus->stCumulativeSystemTime)); - - /* the 30th is Pending signals */ - pcSignalstr = pStatItem[iPendingSigPos]; - pstPrPsStatus->ulSigPend = 0; - while (*pcSignalstr != '\0') { - pstPrPsStatus->ulSigPend = 10 * pstPrPsStatus->ulSigPend + (*pcSignalstr - '0'); - pcSignalstr++; - } - - /* the 31th is Held signals */ - pcSignalstr = pStatItem[iHeldSigPos]; - pstPrPsStatus->ulSigHold = 0; - while (*pcSignalstr != '\0') { - pstPrPsStatus->ulSigHold = 10 * pstPrPsStatus->ulSigHold + (*pcSignalstr - '0'); - pcSignalstr++; - } - - return RET_OK; -} - -/* - * get the process run time - * in : struct BBOX_ELF_PRPSSTATUS *pstPrPsStatus - pointer to structure that store process status information. - * return RET_OK or RET_ERR. - */ -static int BBOX_FillPsStatusTimeInfo(struct BBOX_ELF_PRPSSTATUS* pstPrPsStatus) -{ - ssize_t iReadSize = RET_ERR; - int iStatFileFd = -1; - int iResult = RET_ERR; - char szReadFile[BBOX_BUFF_LITTLE_SIZE]; - errno_t rc = EOK; - if (NULL == pstPrPsStatus) { - bbox_print(PRINT_ERR, "BBOX_FillPsStatusTimeInfo parameters is invalid: pstPrPsStatus is NULL.\n"); - - return RET_ERR; - } - - rc = memset_s(szReadFile, sizeof(szReadFile), 0, sizeof(szReadFile)); - securec_check_c(rc, "\0", "\0"); - - /* read /proc/self/stat */ - BBOX_NOINTR(iStatFileFd = sys_open(THREAD_SELF_STAT_FILE, O_RDONLY, 0)); - if (iStatFileFd < 0) { - bbox_print(PRINT_ERR, "sys_open is failed iStatFileFd = %d.\n", iStatFileFd); - - return RET_ERR; - } - - do { - iReadSize = RET_ERR; - rc = memset_s(szReadFile, sizeof(szReadFile), 0, sizeof(szReadFile)); - securec_check_c(rc, "\0", "\0"); - - BBOX_NOINTR(iReadSize = sys_read(iStatFileFd, szReadFile, sizeof(szReadFile))); - if (iReadSize > 0) { - iResult = BBOX_SetPsStatusTime(szReadFile, iReadSize, pstPrPsStatus); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_SetPsStatusTime is failed, iResult = %d.\n", iResult); - BBOX_NOINTR(sys_close(iStatFileFd)); - return RET_ERR; - } - } else if (iReadSize < 0) { - bbox_print(PRINT_ERR, "failed to read status file, iResult = %zd.\n", iReadSize); - BBOX_NOINTR(sys_close(iStatFileFd)); - return RET_ERR; - } - } while (iReadSize > 0); - - BBOX_NOINTR(sys_close(iStatFileFd)); - - return RET_OK; -} - -/* - * get register information of parent process, get user data structure information. - * in : struct BBOX_ELF_PRPSINFO *pstPrPsInfo - pointer to structure that store process status information. - * pid_t tMainPid - main process pid. - * return RET_OK or RET_ERR. - */ -static int BBOX_FillPrPsInfo(struct BBOX_ELF_PRPSINFO* pstPrPsInfo, pid_t tMainPid) -{ - char szBuff[BBOX_BUFF_SIZE]; - - ssize_t iReadSize = 0; - ssize_t iLen = 0; - int iCommandLineFileFd = -1; - char* pExePathName = szBuff; - char* pTemp = NULL; - errno_t rc = EOK; - if (NULL == pstPrPsInfo) { - bbox_print(PRINT_ERR, "BBOX_FillPrPsInfo parameters is invalid: pstPrPsInfo is NULL.\n"); - - return RET_ERR; - } - - rc = memset_s(pstPrPsInfo, sizeof(struct BBOX_ELF_PRPSINFO), 0, sizeof(struct BBOX_ELF_PRPSINFO)); - securec_check_c(rc, "\0", "\0"); - - pstPrPsInfo->cSname = 'R'; - pstPrPsInfo->cNice = (signed char)sys_getpriority(PRIO_PROCESS, 0); -#if (defined(__x86_64__)) || (defined(__aarch64__)) - pstPrPsInfo->tUid = (uint32_t)sys_geteuid(); - pstPrPsInfo->tGid = (uint32_t)sys_getegid(); -#elif (defined(__i386__)) || (defined(__ARM_ARCH_5TE__)) || (defined(__ARM_ARCH_7A__)) - pstPrPsInfo->tUid = (uint16_t)sys_geteuid(); - pstPrPsInfo->tGid = (uint16_t)sys_getegid(); -#endif - pstPrPsInfo->tpid = tMainPid; - pstPrPsInfo->tPpid = sys_getppid(); - pstPrPsInfo->tPgrp = sys_getpgrp(); - pstPrPsInfo->tSid = sys_getsid(0); - - rc = memset_s(szBuff, sizeof(szBuff), 0, sizeof(szBuff)); - securec_check_c(rc, "\0", "\0"); - - iReadSize = sys_readlink(THREAD_SELF_EXE_FILE, szBuff, sizeof(szBuff)); - iLen = 0; - for (pTemp = szBuff; (*pTemp != '\000') && ((iReadSize--) > 0); pTemp++) { - /* get the command name of the program to run (/bin/bash --> bash) */ - if (*pTemp == '/') { - pExePathName = pTemp + 1; - iLen = 0; - } else { - iLen++; - } - } - rc = memcpy_s(pstPrPsInfo->cFname, - sizeof(pstPrPsInfo->cFname), - pExePathName, - (iLen > (ssize_t)sizeof(pstPrPsInfo->cFname) ? sizeof(pstPrPsInfo->cFname) : iLen)); - securec_check_c(rc, "\0", "\0"); - - /* read /proc/self/command, get parameter list of command. */ - BBOX_NO_INTR(iCommandLineFileFd = sys_open(THREAD_SELF_COMMAND_LINE_FILE, O_RDONLY, 0)); - if (iCommandLineFileFd < 0) { - bbox_print(PRINT_ERR, "sys_open is failed: iCommandLineFileFd = %d.\n", iCommandLineFileFd); - - return RET_ERR; - } - - BBOX_NO_INTR(iReadSize = sys_read(iCommandLineFileFd, pstPrPsInfo->cPsargs, sizeof(pstPrPsInfo->cPsargs))); - if (iReadSize < 0) { - BBOX_NO_INTR(sys_close(iCommandLineFileFd)); - return RET_ERR; - } - - for (pTemp = pstPrPsInfo->cPsargs; (iReadSize--) > 0; pTemp++) { - /* convert '\0' to ' ' so that all of it can be print out one time. */ - if (*pTemp == '\000') { - *pTemp = ' '; - } - } - - BBOX_NO_INTR(sys_close(iCommandLineFileFd)); - - bbox_print(PRINT_LOG, "Fill Prpsinfo Info success.\n"); - return RET_OK; -} - -/* - * get process register content. - * in : Frame *pFrame - pointer for writing core file. - * struct BBOX_ELF_NOTE_INFO *pstNoteInfo - pointer to structure of note segment distription. - * pid_t *ptPids - pointer to process id array. - * int iSegmentNum - count of mapping segment in address space. - * int *piPhdrSum - sum count of mapping segment and VDSO segment in core file. - * int iThreadNum - size of process array. - * return RET_OK or RET_ERR. - */ -static int BBOX_FillPrPsStatusRegs(Frame* pFrame, struct BBOX_ELF_NOTE_INFO* pstNoteInfo, pid_t* ptPids, int iThreadNum) -{ - char acBuff[BBOX_BUFF_LITTLE_SIZE]; - unsigned int uCount = 0; - errno_t rc = EOK; - - if (NULL == pFrame || NULL == pstNoteInfo || NULL == ptPids || iThreadNum <= 0) { - bbox_print(PRINT_ERR, - "BBOX_FillPrPsStatusRegs parameters is invalid: pFrame, pstNoteInfo or " \ - "ptPids is NULL, iThreadNum = %d.\n", - iThreadNum); - - return RET_ERR; - } - - struct BBOX_THREAD_NOTE_INFO* pstThreadNoteInfo = pstNoteInfo->pstThreadNoteInfo; - - for (uCount = 0; uCount < (unsigned int)iThreadNum; uCount++) { - rc = memset_s(acBuff, BBOX_BUFF_LITTLE_SIZE, 0xFF, sizeof(acBuff)); - securec_check_c(rc, "\0", "\0"); - -#if defined(__aarch64__) - /* get cpu register information of pid[i], run if err, try best to create core file. */ - void* pregset = (void*)NT_PRSTATUS; - struct iovec io_vec; - - io_vec.iov_base = acBuff; - io_vec.iov_len = sizeof(struct CPURegs); - if (RET_OK == sys_ptrace(PTRACE_GETREGSET, ptPids[uCount], pregset, &io_vec)) { - rc = memcpy_s(&((pstThreadNoteInfo + uCount)->stPrpsstatus.stRegisters), - sizeof(struct CPURegs), - acBuff, - sizeof(struct CPURegs)); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(acBuff, sizeof(acBuff), 0xFF, sizeof(acBuff)); - securec_check_c(rc, "\0", "\0"); - } -#else - /* get cpu register information of pid[i], run if err, try best to create core file. */ - if (RET_OK == sys_ptrace(PTRACE_GETREGS, ptPids[uCount], acBuff, acBuff)) { - rc = memcpy_s(&((pstThreadNoteInfo + uCount)->stPrpsstatus.stRegisters), - sizeof(struct CPURegs), - acBuff, - sizeof(struct CPURegs)); - securec_check_c(rc, "\0", "\0"); - - if (ptPids[uCount] == pstNoteInfo->tMainPid) { - SET_FRAME(*(Frame*)pFrame, (pstThreadNoteInfo + uCount)->stPrpsstatus.stRegisters); - } - - rc = memset_s(acBuff, BBOX_BUFF_LITTLE_SIZE, 0xFF, sizeof(acBuff)); - securec_check_c(rc, "\0", "\0"); - } - - /* get fpu register information of pid[i], run if err, try best to create core file. */ - if (RET_OK == sys_ptrace(PTRACE_GETFPREGS, ptPids[uCount], acBuff, acBuff)) { - rc = memcpy_s(&((pstThreadNoteInfo + uCount)->stFpRegisters), - sizeof(struct BBOX_FPREGSET), - acBuff, - sizeof(struct BBOX_FPREGSET)); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(acBuff, BBOX_BUFF_LITTLE_SIZE, 0xFF, sizeof(acBuff)); - securec_check_c(rc, "\0", "\0"); - } -#endif - -#if (defined(__i386__)) - - /* get sse register information of pid[i], run if err, try best to create core file. */ - if (RET_OK == sys_ptrace(PTRACE_GETFPXREGS, ptPids[uCount], acBuff, acBuff)) { - rc = memcpy_s(&((pstThreadNoteInfo + uCount)->stFpxRegisters), - sizeof(struct BBOX_FPXREGSET), - acBuff, - sizeof(struct BBOX_FPXREGSET)); - securec_check_c(rc, "\0", "\0"); - pstNoteInfo->iFpxRegistersFlag = BBOX_TRUE; - } else { - pstNoteInfo->iFpxRegistersFlag = BBOX_FALSE; - } -#else - - /* sse register information is stored in sse structure in x86-64. */ - pstNoteInfo->iFpxRegistersFlag = BBOX_FALSE; -#endif - - (pstThreadNoteInfo + uCount)->stPrpsstatus.tpid = ptPids[uCount]; - } - - return RET_OK; -} - -/* - * get user data structure information. - * in : struct BBOX_CORE_USER *pstCoreUser - pointer to strcuture that store user data. - * struct CPURegs *pstThreadRegs - struct pointer that store parent process register information. - * pid_t *ptPids - pointer to process id array. - * return RET_OK or RET_ERR. - */ -static int BBOX_GetParentRegs(struct BBOX_CORE_USER* pstCoreUser, struct CPURegs* pstThreadRegs, pid_t* ptPids) -{ - int iCount = 0; - - if (NULL == pstCoreUser || NULL == pstThreadRegs || NULL == ptPids) { - bbox_print(PRINT_ERR, - "BBOX_GetParentRegs parameters is invalid: pstCoreUser, " \ - "pstThreadRegs or ptPids is NULL.\n"); - - return RET_ERR; - } - - for (iCount = 0; iCount < (int)(sizeof(struct BBOX_CORE_USER) / sizeof(int)); iCount++) { - /* get register information of parent process and copy it into user data structure, - run if err, try best to create core file. */ - (void)sys_ptrace( - PTRACE_PEEKUSER, ptPids[0], (void*)(iCount * sizeof(int)), ((char*)pstCoreUser) + iCount * sizeof(int)); - } - - errno_t rc = memcpy_s(&(pstCoreUser->stRegisters), sizeof(struct CPURegs), pstThreadRegs, sizeof(struct CPURegs)); - securec_check_c(rc, "\0", "\0"); - - return RET_OK; -} - -/* - * fill note segment structure information of core file. - * in : Frame *pFrame - pointer of backstack information - * struct BBOX_ELF_NOTE_INFO *pstNoteInfo - pointer to execute note structure. - * pid_t *ptPids - pointer to process id. - * int iAuxvNum - count of Auxv that need to be written into core file. - * return RET_OK or RET_ERR. - */ -static int BBOX_FillNoteInfo(Frame* pFrame, struct BBOX_ELF_NOTE_INFO* pstNoteInfo, pid_t* ptPids, int iAuxvNum) -{ - unsigned int uCount = 0; - int iResult = -1; - struct BBOX_ELF_PRPSSTATUS stTempPrPsStatus; - errno_t rc = EOK; - if (NULL == pFrame || NULL == pstNoteInfo || NULL == ptPids || iAuxvNum < 0) { - bbox_print(PRINT_ERR, - "BBOX_FillNoteInfo parameters is invalid: pFrame, "\ - "pstNoteInfo or ptPids is NULL, iAuxvNum = %d.\n", - iAuxvNum); - - return RET_ERR; - } - - pstNoteInfo->iAuxvNoteInfoNum = iAuxvNum; - rc = memset_s(&(stTempPrPsStatus), sizeof(struct BBOX_ELF_PRPSSTATUS), 0, sizeof(struct BBOX_ELF_PRPSSTATUS)); - securec_check_c(rc, "\0", "\0"); - - rc = memset_s(&(pstNoteInfo->stCoreUser), sizeof(struct BBOX_CORE_USER), 0, sizeof(struct BBOX_CORE_USER)); - securec_check_c(rc, "\0", "\0"); - - rc = memset_s(&(pstNoteInfo->stPrpsinfo), sizeof(struct BBOX_ELF_PRPSINFO), 0, sizeof(struct BBOX_ELF_PRPSINFO)); - securec_check_c(rc, "\0", "\0"); - - struct BBOX_ELF_PRPSINFO* pstPrPsInfo = &(pstNoteInfo->stPrpsinfo); - struct BBOX_THREAD_NOTE_INFO* pstThreadPrpsstatus = pstNoteInfo->pstThreadNoteInfo; - int iThreadNum = pstNoteInfo->iThreadNoteInfoNum; - - /* get run status, priority, group id, parent process id of a process - and record them into struct BBOX_ELF_PRPSINFO. */ - iResult = BBOX_FillPrPsInfo(pstPrPsInfo, pstNoteInfo->tMainPid); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_FillPrPsInfo is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - /* get register, FPU and SSE information of process and record them into struct BBOX_THREAD_NOTE_INFO. */ - iResult = BBOX_FillPrPsStatusRegs(pFrame, pstNoteInfo, ptPids, iThreadNum); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_FillPrPsStatusRegs is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - /* get user data structure information and record it into struct BBOX_CORE_USER. */ - iResult = - BBOX_GetParentRegs(&(pstNoteInfo->stCoreUser), &(pstThreadPrpsstatus[0].stPrpsstatus.stRegisters), ptPids); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_GetParentRegs is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - /* get process time information and record it into struct BBOX_ELF_PRPSSTATUS. */ - iResult = BBOX_FillPsStatusTimeInfo(&stTempPrPsStatus); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_FillPsStatusTimeInfo is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - for (uCount = 0; uCount < (unsigned int)iThreadNum; uCount++) { - (pstThreadPrpsstatus + uCount)->stPrpsstatus.tPpid = pstNoteInfo->stPrpsinfo.tPpid; - (pstThreadPrpsstatus + uCount)->stPrpsstatus.tPgrp = pstNoteInfo->stPrpsinfo.tPgrp; - (pstThreadPrpsstatus + uCount)->stPrpsstatus.tSid = pstNoteInfo->stPrpsinfo.tSid; - (pstThreadPrpsstatus + uCount)->stPrpsstatus.tFpvalid = BBOX_TRUE; - - (pstThreadPrpsstatus + uCount)->stPrpsstatus.stUserTime.lTvSec = stTempPrPsStatus.stUserTime.lTvSec; - (pstThreadPrpsstatus + uCount)->stPrpsstatus.stUserTime.lTvMicroSec = stTempPrPsStatus.stUserTime.lTvMicroSec; - - (pstThreadPrpsstatus + uCount)->stPrpsstatus.stSystemTime.lTvSec = stTempPrPsStatus.stSystemTime.lTvSec; - (pstThreadPrpsstatus + uCount)->stPrpsstatus.stSystemTime.lTvMicroSec = - stTempPrPsStatus.stSystemTime.lTvMicroSec; - - (pstThreadPrpsstatus + uCount)->stPrpsstatus.stCumulativeUserTime.lTvSec = - stTempPrPsStatus.stCumulativeUserTime.lTvSec; - (pstThreadPrpsstatus + uCount)->stPrpsstatus.stCumulativeUserTime.lTvMicroSec = - stTempPrPsStatus.stCumulativeUserTime.lTvMicroSec; - - (pstThreadPrpsstatus + uCount)->stPrpsstatus.stCumulativeSystemTime.lTvSec = - stTempPrPsStatus.stCumulativeSystemTime.lTvSec; - (pstThreadPrpsstatus + uCount)->stPrpsstatus.stCumulativeSystemTime.lTvMicroSec = - stTempPrPsStatus.stCumulativeSystemTime.lTvMicroSec; - } - - return RET_OK; -} - -/* - * create symbol table - * in : char *pBuffer - buffer - * unsigned int uiBufLen - buffer size - * return RET_OK or RET_ERR. - */ -int BBOX_GenerateStrTab(char* pBuffer, unsigned int uiBufLen) -{ - int iResult = 0; - unsigned int uCount = 0; - unsigned int uiAllStringSz = 0; - - /* string symbol table */ - char* pacShName[BBOX_SECTION_NUM] = {BBOX_ADDON_INFO, BBOX_LOG, BBOX_STR_TAB}; - - /* concatenate the above strings into the buffer, take care '\0'. */ - for (uCount = 0; uCount < BBOX_SECTION_NUM; uCount++) { - iResult = bbox_snprintf(pBuffer, uiBufLen, pacShName[uCount]); - if (iResult <= 0 || iResult > (int)uiBufLen) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - - return RET_ERR; - } - - pBuffer += iResult; - uiBufLen -= iResult; - uiAllStringSz += iResult; - } - - return uiAllStringSz; -} - -/* - * get content that be written into section. - * return RET_OK or RET_ERR. - */ -void BBOX_FillSectionInfo(void) -{ - int iResult = RET_ERR; - errno_t rc = EOK; - - /* create symbol table, if occur err, don't return ,run and try best to create core file. */ - iResult = BBOX_GenerateStrTab(g_acBboxStrTabInfo, sizeof(g_acBboxStrTabInfo)); - if (iResult < 0) { - bbox_print(PRINT_ERR, "_BBOX_GenerateStrTab is failed.\n"); - } - - bbox_print(PRINT_LOG, "Generate section string table successful.\n"); - - /* Get system information, continue if failed, try best to create core file. */ - iResult = _BBOX_GetAddonInfo(g_acBboxAddonInfo, sizeof(g_acBboxAddonInfo)); - if (iResult < 0) { - bbox_print(PRINT_ERR, "_BBOX_GetAddonInfo is failed.\n"); - } - - bbox_print(PRINT_LOG, "Generate addition system info successful.\n"); - - g_stElfSectionInfo.uiSectionNum = BBOX_SECTION_NUM; - g_stElfSectionInfo.pstSection = g_stSectionInfo; - - /* BBOX_ADDONINFO */ - g_stSectionInfo[0].uiSectionType = SHT_NOTE; - g_stSectionInfo[0].pacSectionDesc = g_acBboxAddonInfo; - g_stSectionInfo[0].uiSectionDescSize = sizeof(g_acBboxAddonInfo); - g_stSectionInfo[0].uiSectionNameSize = sizeof(BBOX_ADDON_INFO); - rc = strncpy_s( - g_stSectionInfo[0].acSectionName, BBOX_SECTION_NAME_LEN, BBOX_ADDON_INFO, g_stSectionInfo[0].uiSectionNameSize); - securec_check_c(rc, "\0", "\0"); - - /* BBOX_LOG */ - g_stSectionInfo[1].uiSectionType = SHT_NOTE; - g_stSectionInfo[1].pacSectionDesc = g_acBBoxLog; - g_stSectionInfo[1].uiSectionDescSize = sizeof(g_acBBoxLog); - g_stSectionInfo[1].uiSectionNameSize = sizeof(BBOX_LOG); - rc = strncpy_s( - g_stSectionInfo[1].acSectionName, BBOX_SECTION_NAME_LEN, BBOX_LOG, g_stSectionInfo[1].uiSectionNameSize); - securec_check_c(rc, "\0", "\0"); - - /* .shstrtab */ - g_stSectionInfo[2].uiSectionType = SHT_STRTAB; - g_stSectionInfo[2].pacSectionDesc = g_acBboxStrTabInfo; - g_stSectionInfo[2].uiSectionDescSize = sizeof(g_acBboxStrTabInfo); - g_stSectionInfo[2].uiSectionNameSize = sizeof(BBOX_STR_TAB); - rc = strncpy_s( - g_stSectionInfo[2].acSectionName, BBOX_SECTION_NAME_LEN, BBOX_STR_TAB, g_stSectionInfo[2].uiSectionNameSize); - securec_check_c(rc, "\0", "\0"); - - bbox_print(PRINT_LOG, "Fill all section successful.\n"); - - return; -} - -/* - * fill every structure written into core file, include mapping, note, VDSO. - * in : Frame *pFrame - Write the structure pointer to the core file - * struct BBOX_VM_MAPS *pstVmMappingSegment - Pointer to the structure that describes the mapping segment - * int iSegmentNum - The number of mapping segments in the address space - * int *piPhdrSum - Sum of mapping and VDSO in core file - * pid_t *ptPids - A pointer to an array of process Numbers - * struct BBOX_ELF_NOTE_INFO *pstNoteInfo - Pointer to the structure that describes the note segment - * union BBOX_VM_VDSO *pstVmVDSO - Pointer to the structure that describes the VDSO segment - */ -static int BBOX_FillAllInfoOfCoreFile(Frame* pFrame, struct BBOX_VM_MAPS* pstVmMappingSegment, int* piSegmentNum, - int* piPhdrSum, pid_t* ptPids, struct BBOX_ELF_NOTE_INFO* pstNoteInfo, union BBOX_VM_VDSO* pstVmVDSO) -{ - int iResult = 1; - int iValidSegmentNum = 0; - int iAuxvNum = 0; - int iExtraPhdrNum = 0; - - if (NULL == pFrame || NULL == pstVmMappingSegment || NULL == piPhdrSum || NULL == ptPids || NULL == pstNoteInfo || - NULL == pstVmVDSO || NULL == piSegmentNum) { - bbox_print(PRINT_ERR, - "BBOX_FillAllInfoOfCoreFile parameters is invalid: pFrame, pstVmMappingSegment, " \ - "piPhdrSum, ptPids, pstNoteInfo, pstVmVDSO or piSegmentNum is NULL.\n"); - return RET_ERR; - } - - /* read /proc/self/maps, fill structure that discribes process address space information. */ - iResult = BBOX_FillVmMappingInfo(pstVmMappingSegment, piSegmentNum, &iValidSegmentNum); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_FillVmMappingInfo is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - - /* get number of auxv written into core file in /proc/self/auxv, record VDSO address. - Determines whether VDSO is written to the core file and the number of segments written, - record it into iSegmentNum */ - iResult = BBOX_FillVDSOInfo(&iAuxvNum, &iExtraPhdrNum, *piSegmentNum, pstVmVDSO, pstVmMappingSegment); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_FillVDSOInfo is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - - /* calculate all segment that need to be written into core file except note segment */ - *piPhdrSum = iValidSegmentNum + iExtraPhdrNum; - bbox_print(PRINT_DBG, - "Get all Phdr numbers success, iValidSegmentNum = %d, iExtraPhdrNum = %d, *piPhdrSum=%d.\n", - iValidSegmentNum, - iExtraPhdrNum, - *piPhdrSum); - - /* Fill struct BBOX_ELF_NOTE_INFO */ - iResult = BBOX_FillNoteInfo(pFrame, pstNoteInfo, ptPids, iAuxvNum); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_FillNoteInfo is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - - /* fill section */ - BBOX_FillSectionInfo(); - - bbox_print(PRINT_LOG, "Fill Note Info success.\n"); - - return RET_OK; -} - -/* - * create and open core file, get fd. - * in : Frame *pFrame - Pointer to the structure of the core file - * char *pFileName - Pointer to an array of core file names - * struct BBOX_WRITE_FDS *pstFileWriteFd - Pointer to a structure that describes the properties of a core file - */ -static int BBOX_OpenCoreFile(Frame* pFrame, const char* pFileName, struct BBOX_WRITE_FDS* pstFileWriteFd) -{ - char szCmd[BBOX_CMD_LEN]; - errno_t rc = EOK; - - if (NULL == pFrame || NULL == pstFileWriteFd) { - bbox_print(PRINT_ERR, - "BBOX_OpenCoreFile parameters is invalid: pFrame or "\ - "pstFileWriteFd is NULL.\n"); - return RET_ERR; - } - - rc = memset_s(szCmd, BBOX_CMD_LEN, 0, sizeof(szCmd)); - securec_check_c(rc, "\0", "\0"); - - /* If the user does not define the file name, set it to core.tid.lz4 */ - if (NULL != pFileName) { - bbox_snprintf(szCmd, BBOX_CMD_LEN, COMPRESSION_CMD, 1, pFileName); - } else { - bbox_snprintf(szCmd, BBOX_CMD_LEN, COMPRESSION_CMD_WITH_FILENAME, 1, ((Frame*)pFrame)->tid); - } - - /* create core file using the way of compression while writing */ - pstFileWriteFd->iWriteFd = sys_popen(szCmd, "w"); - if (pstFileWriteFd->iWriteFd < 0) { - bbox_print(PRINT_ERR, "sys_popen is failed, pstFileWriteFd->iWriteFd = %d.\n", pstFileWriteFd->iWriteFd); - return RET_ERR; - } - - pstFileWriteFd->uiMaxLength = ~(size_t)0; - - bbox_print(PRINT_LOG, "Open core file success.\n"); - - return RET_OK; -} - -/* - * write to file - * in : struct BBOX_WRITE_FDS *pstWriteFds - A pointer to core file. - * void *pWriteData - Points to what will be written to the file - * size_t uiWriteSize - The size of what will be written to the file - */ -static ssize_t BBOX_DoWrite(struct BBOX_WRITE_FDS* pstWriteFds, void* pWriteData, size_t uiWriteSize) -{ - ssize_t iRet = 0; - ssize_t iWriteCount = 0; - const ssize_t iMaxSize = (1024 * 1024 * 1024); // 1G - size_t uiSize = 0; - - if (NULL == pstWriteFds || NULL == pWriteData) { - bbox_print(PRINT_ERR, - "BBOX_DoWrite parameters is invalid: pstWriteFds or " - "pWriteData is NULL.\n"); - return RET_ERR; - } - - while (uiWriteSize) { - uiSize = uiWriteSize; - if (uiSize > (size_t)iMaxSize) { - uiSize = iMaxSize; - } - - BBOX_NOINTR(iRet = sys_write(pstWriteFds->iWriteFd, pWriteData, uiSize)); - if (iRet <= 0) { - bbox_print(PRINT_ERR, - "sys_write failed, iRet = %zd, " - "uiSize = %zu, errno = %d.\n", - iRet, - uiSize, - errno); - - return iRet; - } - - iWriteCount += iRet; - uiWriteSize -= iRet; - pWriteData = (char*)pWriteData + iRet; - } - - return iWriteCount; -} - -/* - * The file header Ehdr structure that populates the elf file - * in : BBOX_EHDR *pEhdr - The header structure of the core file - * int iPhdrSum - The number of segments written to the core file in the address space - */ -static int BBOX_FillEhdr(BBOX_EHDR* pstEhdr, int iPhdrSum) -{ - if (NULL == pstEhdr || iPhdrSum <= 0) { - bbox_print(PRINT_ERR, "BBOX_FillEhdr parameters is invalid: pstEhdr maybe NULL, iPhdrSum = %d.\n", iPhdrSum); - return RET_ERR; - } - - pstEhdr->e_ident[0] = ELFMAG0; - pstEhdr->e_ident[1] = (unsigned char)ELFMAG1; - pstEhdr->e_ident[2] = ELFMAG2; - pstEhdr->e_ident[3] = ELFMAG3; - pstEhdr->e_ident[4] = BBOX_ELF_CLASS; - pstEhdr->e_ident[5] = (unsigned char)BBOX_DetermineMsb(); /* Determine whether the system is large or small */ - pstEhdr->e_ident[6] = EV_CURRENT; - pstEhdr->e_type = ET_CORE; - pstEhdr->e_machine = ELF_ARCH; - pstEhdr->e_version = EV_CURRENT; - pstEhdr->e_phoff = sizeof(BBOX_EHDR); - pstEhdr->e_ehsize = sizeof(BBOX_EHDR); - pstEhdr->e_phentsize = sizeof(BBOX_PHDR); - -#if (defined(__i386__)) - pstEhdr->e_phnum = (Elf32_Half)(iPhdrSum + 1); /* Number of memory address space segments plus note segments */ - pstEhdr->e_shnum = (Elf32_Half)(BBOX_SECTION_NUM); -#elif (defined(__x86_64__)) || (defined(__ARM_ARCH_5TE__)) || (defined(__ARM_ARCH_7A__)) || (defined(__aarch64__)) - pstEhdr->e_phnum = (Elf64_Half)(iPhdrSum + 1); /* Number of memory address space segments plus note segments */ - pstEhdr->e_shnum = (Elf64_Half)(BBOX_SECTION_NUM); -#endif - - pstEhdr->e_shoff = sizeof(BBOX_EHDR) + (iPhdrSum + 1) * sizeof(BBOX_PHDR); - pstEhdr->e_shentsize = sizeof(BBOX_SHDR); - - pstEhdr->e_shstrndx = BBOX_SHSTR_INDEX; - - bbox_print(PRINT_LOG, "Fill Elf Ehdr success.\n"); - - return RET_OK; -} - -/* - * write file header of core to core file. - * in : int iPhdrSum - The number of segment in the core file - * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to core file - */ -static int BBOX_WriteElfEhdr(int iPhdrSum, struct BBOX_WRITE_FDS* pstFileWriteFd) -{ - int iResult = RET_ERR; - ssize_t iWriteSize = -1; - BBOX_EHDR stElfCoreHead; - errno_t rc = EOK; - if (NULL == pstFileWriteFd || iPhdrSum <= 0) { - bbox_print(PRINT_ERR, - "BBOX_WriteElfEhdr parameters is invalid: pstFileWriteFd may be NULL, " \ - "iPhdrSum = %d.\n", - iPhdrSum); - - return RET_ERR; - } - - rc = memset_s(&stElfCoreHead, sizeof(BBOX_EHDR), 0, sizeof(BBOX_EHDR)); - securec_check_c(rc, "\0", "\0"); - - iResult = BBOX_FillEhdr(&stElfCoreHead, iPhdrSum); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_FillEhdr is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - iWriteSize = BBOX_DoWrite(pstFileWriteFd, &stElfCoreHead, sizeof(BBOX_EHDR)); - if (iWriteSize == RET_ERR || sizeof(BBOX_EHDR) != iWriteSize) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iWriteSize = %zd\n", iWriteSize); - - return RET_ERR; - } - - bbox_print(PRINT_LOG, "Write Ehdr info to the core file success.\n"); - - return RET_OK; -} - -/* - * Fill the header of the note segment of the core file describes the structure - * in : struct BBOX_ELF_NOTE_INFO *pstNoteInfo - The content structure of the note segment - * int iPhdrSum - The number of segments written to the core file in the address space - * size_t *puiOffset - The starting offset of the next segment - * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to core file - */ -static int BBOX_WriteNotePhdr( - struct BBOX_ELF_NOTE_INFO* pstNoteInfo, int iPhdrSum, size_t* puiOffset, struct BBOX_WRITE_FDS* pstFileWriteFd) -{ - size_t uiOffSize = 0; - size_t uiFileSize = 0; - size_t uiCoreUserSize = 0; - size_t uiNoteSize = 0; - size_t uiAuxvSize = 0; - size_t uiNoteAlign = 0; - ssize_t iWriteSize = -1; - int iPageSize = sys_sysconf(_SC_PAGESIZE); /* Gets the system page size */ - BBOX_PHDR stElfPhdr; - - if (NULL == pstNoteInfo || NULL == puiOffset || NULL == pstFileWriteFd || iPhdrSum <= 0) { - bbox_print(PRINT_ERR, - "BBOX_WriteNotePhdr parameters is invalid: pstNoteInfo, puiOffset or pstFileWriteFd is NULL, " \ - "iPhdrSum = %d.\n", - iPhdrSum); - - return RET_ERR; - } - - errno_t rc = memset_s(&stElfPhdr, sizeof(BBOX_PHDR), 0, sizeof(BBOX_PHDR)); - securec_check_c(rc, "\0", "\0"); - - /* Calculates the starting position and size of the note segment in the core file */ - uiOffSize = sizeof(BBOX_EHDR) + (iPhdrSum + 1) * sizeof(BBOX_PHDR) + 3 * sizeof(BBOX_SHDR); - - /* Calculate the size of the user data */ - uiCoreUserSize = sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_CORE_USER); - - /* Calculate the size of the BBOX_ELF_PRPSSTATUS */ -#if defined(__aarch64__) - uiNoteSize = (pstNoteInfo->iThreadNoteInfoNum) * - (sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_ELF_PRPSSTATUS)); -#else - uiNoteSize = (pstNoteInfo->iThreadNoteInfoNum) * - (sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_ELF_PRPSSTATUS) + sizeof(BBOX_NHDR) + - BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_FPREGSET)); -#endif - - if (pstNoteInfo->iFpxRegistersFlag) { - /* If the SSE register exists, add its size */ - uiNoteSize += (pstNoteInfo->iThreadNoteInfoNum) * - (sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_FPXREGSET)); - } - - /* Calculates the size of the Auxv written */ - uiAuxvSize = sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + (pstNoteInfo->iAuxvNoteInfoNum) * sizeof(BBOX_AUXV_T); - uiFileSize = sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_ELF_PRPSINFO) + uiCoreUserSize + - uiNoteSize + uiAuxvSize; - - stElfPhdr.p_type = PT_NOTE; - stElfPhdr.p_offset = uiOffSize; - stElfPhdr.p_filesz = uiFileSize; - *puiOffset = uiOffSize + uiFileSize; - - iWriteSize = BBOX_DoWrite(pstFileWriteFd, &stElfPhdr, sizeof(BBOX_PHDR)); - if (iWriteSize == RET_ERR || sizeof(BBOX_PHDR) != iWriteSize) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iWriteSize = %zd.\n", iWriteSize); - return RET_ERR; - } - - /* Calculate the size of the page alignment to fill, and calculate the starting position of the next segment */ - stElfPhdr.p_align = iPageSize; - uiNoteAlign = stElfPhdr.p_align - ((*puiOffset) % stElfPhdr.p_align); - if (uiNoteAlign == stElfPhdr.p_align) { - uiNoteAlign = 0; - } - - pstNoteInfo->uiNoteAlign = uiNoteAlign; - (*puiOffset) += uiNoteAlign; - - return RET_OK; -} - -/* - * Fill the header of the note segment of the core file describes the structure - * in : struct BBOX_VM_MAPS *pstVmMappingSegment - The content structure of the note segment - * int iSegmentNum - The number of segments written to the core file in the address space - * size_t *puiOffset - The starting offset of the next segment - * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to core file - */ -static int BBOX_WriteVmSegmentPhdr( - struct BBOX_VM_MAPS* pstVmMappingSegment, int iSegmentNum, size_t* puiOffset, struct BBOX_WRITE_FDS* pstFileWriteFd) -{ - size_t uiFileSize = 0; - ssize_t iWriteSize = -1; - unsigned int uCount = 0; - int iPageSize = sys_sysconf(_SC_PAGESIZE); - BBOX_PHDR stElfPhdr; - - if (NULL == pstVmMappingSegment || NULL == puiOffset || NULL == pstFileWriteFd || iSegmentNum <= 0) { - bbox_print(PRINT_ERR, - "BBOX_WriteVmSegmentPhdr parameters is invalid: pstVmMappingSegment, puiOffset or " - "pstFileWriteFd is NULL, iSegmentNum = %d.\n", - iSegmentNum); - return RET_ERR; - } - - errno_t rc = memset_s(&stElfPhdr, sizeof(BBOX_PHDR), 0, sizeof(BBOX_PHDR)); - securec_check_c(rc, "\0", "\0"); - - stElfPhdr.p_type = PT_LOAD; - stElfPhdr.p_align = iPageSize; - stElfPhdr.p_paddr = 0; - - for (uCount = 0; uCount < (unsigned int)iSegmentNum; uCount++) { - if (pstVmMappingSegment[uCount].iIsRemoveFlags == 0) { - /* calculate size */ - uiFileSize = (pstVmMappingSegment[uCount].uiEndAddress) - (pstVmMappingSegment[uCount].uiStartAddress); - stElfPhdr.p_offset = *puiOffset; /* offset */ - stElfPhdr.p_vaddr = (pstVmMappingSegment[uCount].uiStartAddress); /* start address in address space of segment */ - stElfPhdr.p_memsz = uiFileSize; /* size of segment in address space */ - - uiFileSize = (pstVmMappingSegment[uCount].uiWriteSize); /* size of segment in file */ - stElfPhdr.p_filesz = uiFileSize; - stElfPhdr.p_flags = (pstVmMappingSegment[uCount].iFlags) & PF_MASK; - - (*puiOffset) += uiFileSize; /* next offset of segment. */ - - iWriteSize = BBOX_DoWrite(pstFileWriteFd, &stElfPhdr, sizeof(BBOX_PHDR)); - if (iWriteSize == RET_ERR || sizeof(BBOX_PHDR) != iWriteSize) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iWriteSize = %zd.\n", iWriteSize); - - return RET_ERR; - } - } - } - - return RET_OK; -} - -/* - * Writes a part of the VDSO segment to the core file - * in : struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to the structure of the core file - * union BBOX_VM_VDSO *pstVmVDSO - The structure that points to the VDSO segment - * size_t *puiOffset - The starting offset pointer of the next segment - * return RET_OK or RET_ERR. - */ -static int BBOX_WriteVDSOPhdr(struct BBOX_WRITE_FDS* pstFileWriteFd, union BBOX_VM_VDSO* pstVmVDSO, size_t* puiOffset) -{ - size_t uiFileSize = 0; - ssize_t iWriteSize = -1; - unsigned int uCount = 0; - BBOX_PHDR stElfPhdr; - - if (NULL == pstFileWriteFd || NULL == pstVmVDSO || NULL == puiOffset) { - bbox_print(PRINT_ERR, - "BBOX_WriteVDSOPhdr parameters is invalid: pstFileWriteFd, pstVmVDSO or " \ - "puiOffset is NULL.\n"); - return RET_ERR; - } - - errno_t rc = memset_s(&stElfPhdr, sizeof(BBOX_PHDR), 0, sizeof(BBOX_PHDR)); - securec_check_c(rc, "\0", "\0"); - - if (pstVmVDSO->uiVDSOAddress) { - BBOX_PHDR* pstVDSOPhdr = (BBOX_PHDR*)(pstVmVDSO->uiVDSOAddress + pstVmVDSO->pVDSOEhdr->e_phoff); - - for (uCount = 0; uCount < pstVmVDSO->pVDSOEhdr->e_phnum; uCount++) { - if (pstVDSOPhdr[uCount].p_type != PT_LOAD) { - /* Write the non-load segment of VDSO to the core file */ - rc = memcpy_s(&stElfPhdr, sizeof(BBOX_PHDR), pstVDSOPhdr + uCount, sizeof(BBOX_PHDR)); - securec_check_c(rc, "\0", "\0"); - - uiFileSize = stElfPhdr.p_filesz; - stElfPhdr.p_offset = *puiOffset; - stElfPhdr.p_paddr = 0; - - iWriteSize = BBOX_DoWrite(pstFileWriteFd, &stElfPhdr, sizeof(BBOX_PHDR)); - if (iWriteSize == RET_ERR || sizeof(BBOX_PHDR) != iWriteSize) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iWriteSize = %zd.\n", iWriteSize); - - return RET_ERR; - } - - (*puiOffset) += uiFileSize; - } - } - } - - return RET_OK; -} - -/* - * Writes the header of section to the core file - * in : int iPhdrSum - The number of program headers in the core file - * struct BBOX_ELF_SECTION *pstSectionInfo - The starting offset pointer of the next segment - * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to the structure of the core file - * return RET_OK or RET_ERR. - */ -static int BBOX_WriteElfShdr( - struct BBOX_ELF_SECTION* pstSectionInfo, size_t* puiOffset, struct BBOX_WRITE_FDS* pstFileWriteFd) -{ - size_t uiOffSize = 0; - size_t uiFileSize = 0; - unsigned int uCount = 0; - int iSecNameOffset = 0; - BBOX_SHDR stElfShdr; - BBOX_SECTION_STRU* pstSection = NULL; - - for (uCount = 0; uCount < pstSectionInfo->uiSectionNum; uCount++) { - pstSection = (pstSectionInfo->pstSection) + uCount; - errno_t rc = memset_s(&stElfShdr, sizeof(BBOX_SHDR), 0, sizeof(BBOX_SHDR)); - securec_check_c(rc, "\0", "\0"); - - uiOffSize = *puiOffset; - uiFileSize = pstSection->uiSectionDescSize; - - /* name of section, it is not a string and it's value is the offset of string in string table. */ - stElfShdr.sh_name = iSecNameOffset; - stElfShdr.sh_type = pstSection->uiSectionType; /* type of section */ - stElfShdr.sh_offset = uiOffSize; - stElfShdr.sh_size = uiFileSize; - - BBOX_WRITE(pstFileWriteFd, &stElfShdr, sizeof(BBOX_SHDR)); - - bbox_print(PRINT_LOG, "Write section[%s] head to fill successed.\n", pstSection->acSectionName); - - *puiOffset = uiOffSize + uiFileSize; - iSecNameOffset += pstSection->uiSectionNameSize; - } - - bbox_print(PRINT_LOG, "Write all section head to fill successed.\n"); - - return RET_OK; -} - -/* - * Writes all the progrem header of core file to the core file - * in : int iPhdrSum - The number of program headers in the core file - * struct BBOX_ELF_NOTE_INFO *pstNoteInfo - A pointer to the structure of note segment - * int iSegmentNum - The number of mapping segments in the address space - * union BBOX_VM_VDSO *pstVmVDSO - A pointer to the structure of VDSO segment - * struct BBOX_VM_MAPS *pstVmMappingSegment - A pointer to the structure of mapping segment - * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to the structure of the core file - * return RET_OK or RET_ERR. - */ -static int BBOX_WriteElfHdr(int iPhdrSum, struct BBOX_ELF_NOTE_INFO* pstNoteInfo, int iSegmentNum, - union BBOX_VM_VDSO* pstVmVDSO, struct BBOX_VM_MAPS* pstVmMappingSegment, struct BBOX_WRITE_FDS* pstFileWriteFd) -{ - int iResult = RET_ERR; - size_t uiOffset = 0; - - if (NULL == pstNoteInfo || NULL == pstVmMappingSegment || NULL == pstVmVDSO || NULL == pstFileWriteFd || - iSegmentNum <= 0 || iPhdrSum <= 0) { - bbox_print(PRINT_ERR, - "BBOX_WriteElfPhdr parameters is invalid: pstNoteInfo, pstVmMappingSegment," \ - "pstVmVDSO or pstFileWriteFd in NULL, iSegmentNum = %d, iPhdrSum = %d.\n", - iSegmentNum, - iPhdrSum); - return RET_ERR; - } - - /* write Phdr of Note to core file. */ - iResult = BBOX_WriteNotePhdr(pstNoteInfo, iPhdrSum, &uiOffset, pstFileWriteFd); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteNotePhdr is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - bbox_print(PRINT_LOG, "Write Note Phdr info to the core file success.\n"); - - /* write Phdr of process address space to core file. */ - iResult = BBOX_WriteVmSegmentPhdr(pstVmMappingSegment, iSegmentNum, &uiOffset, pstFileWriteFd); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteVmSegmentPhdr is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - bbox_print(PRINT_LOG, "Write Vm mapping segment Phdr info to the core file success.\n"); - - /* write Phdr of VDSO to core file. */ - iResult = BBOX_WriteVDSOPhdr(pstFileWriteFd, pstVmVDSO, &uiOffset); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteVDSOPhdr is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - bbox_print(PRINT_LOG, "Write VDSO Phdr info to the core file success.\n"); - - /* write Shdr of section to core file. */ - iResult = BBOX_WriteElfShdr(&g_stElfSectionInfo, &uiOffset, pstFileWriteFd); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteElfShdr is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - bbox_print(PRINT_LOG, "Write section head info to the core file success.\n"); - - return RET_OK; -} - -/* - * Writes the prpsinfo to the core file - * in : struct BBOX_ELF_PRPSINFO *pstPrPsInfo - A pointer to prpsinfo - * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file - * return RET_OK or RET_ERR. - */ -static int BBOX_WritePrPsinfoToFile(struct BBOX_ELF_PRPSINFO* pstPrPsInfo, struct BBOX_WRITE_FDS* pstFileFds) -{ - BBOX_NHDR stElfNhdr; - - if (NULL == pstPrPsInfo || NULL == pstFileFds) { - bbox_print(PRINT_ERR, - "BBOX_WritePrPsinfoToFile parameters is invalid: pstPrPsInfo or pstFileFds is NULL.\n"); - - return RET_ERR; - } - - errno_t rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); - securec_check_c(rc, "\0", "\0"); - - stElfNhdr.n_namesz = BBOX_CORE_NAME_LENGTH; - stElfNhdr.n_descsz = sizeof(struct BBOX_ELF_PRPSINFO); - stElfNhdr.n_type = NT_PRPSINFO; - - if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || - BBOX_DoWrite(pstFileFds, (void*)BBOX_CORE_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH || - BBOX_DoWrite(pstFileFds, pstPrPsInfo, sizeof(struct BBOX_ELF_PRPSINFO)) != sizeof(struct BBOX_ELF_PRPSINFO)) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); - - return RET_ERR; - } - - bbox_print(PRINT_LOG, - "Write Prpsinfo size = %zd to the core file success.\n", - sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_ELF_PRPSINFO)); - - return RET_OK; -} - -/* - * Writes the user data information to the core file - * in : struct BBOX_ELF_PRPSINFO *pPrPsInfo - A pointer to User Core - * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file - * return RET_OK or RET_ERR. - */ -static int BBOX_WriteUserRegistersToFile(struct BBOX_CORE_USER* pstCoreUserInfo, struct BBOX_WRITE_FDS* pstFileFds) -{ - BBOX_NHDR stElfNhdr; - - if (NULL == pstCoreUserInfo || NULL == pstFileFds) { - bbox_print(PRINT_ERR, - "BBOX_WriteUserRegistersToFile parameters is invalid: pstCoreUserInfo or pstFileFds is NULL.\n"); - - return RET_ERR; - } - - errno_t rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); - securec_check_c(rc, "\0", "\0"); - - stElfNhdr.n_namesz = BBOX_CORE_NAME_LENGTH; - stElfNhdr.n_descsz = sizeof(struct BBOX_CORE_USER); - stElfNhdr.n_type = NT_PRXREG; - - if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || - BBOX_DoWrite(pstFileFds, (void*)BBOX_CORE_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH || - BBOX_DoWrite(pstFileFds, pstCoreUserInfo, sizeof(struct BBOX_CORE_USER)) != sizeof(struct BBOX_CORE_USER)) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); - - return RET_ERR; - } - - bbox_print(PRINT_LOG, - "Write UserRegisters size = %zd to the core file success.\n", - sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_CORE_USER)); - - return RET_OK; -} - -/* - * read /proc/self/auxv, write parts of it to a core file - * in : int iAuxvNum - Number of Auxv structures written to the core file - * struct BBOX_WRITE_FDS *pstWriteFds - pointer to core file. - * return RET_OK or RET_ERR. - */ -static int BBOX_WriteAuxvInfoToFile(int iAuxvNum, struct BBOX_WRITE_FDS* pstFileFds) -{ - int iAuxvFd = -1; - ssize_t iReadSize = -1; - unsigned int uCount = 0; - BBOX_NHDR stElfNhdr; - BBOX_AUXV_T stAuxv; - errno_t rc = EOK; - if (NULL == pstFileFds || iAuxvNum < 0) { - bbox_print(PRINT_ERR, - "BBOX_WriteAuxvInfoToFile parameters is invalid: pstFileFds may be NULL," \ - "iAuxvNum = %d.\n", - iAuxvNum); - - return RET_ERR; - } - - rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); - securec_check_c(rc, "\0", "\0"); - - stElfNhdr.n_namesz = BBOX_CORE_NAME_LENGTH; - stElfNhdr.n_descsz = iAuxvNum * sizeof(BBOX_AUXV_T); - stElfNhdr.n_type = NT_AUXV; - - if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || - BBOX_DoWrite(pstFileFds, (void*)BBOX_CORE_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); - - return RET_ERR; - } - - /* read /proc/self/Auxv, write to core file. */ - BBOX_NOINTR(iAuxvFd = sys_open(THREAD_SELF_AUXV_FILE, O_RDONLY, 0)); - if (iAuxvFd < 0) { - bbox_print(PRINT_ERR, "sys_open is failed, iAuxvFd = %d.\n", iAuxvFd); - - return RET_ERR; - } - - for (uCount = 0; uCount < (unsigned int)iAuxvNum; uCount++) { - iReadSize = -1; - rc = memset_s(&stAuxv, sizeof(BBOX_AUXV_T), 0, sizeof(BBOX_AUXV_T)); - securec_check_c(rc, "\0", "\0"); - - BBOX_NOINTR(iReadSize = sys_read(iAuxvFd, &stAuxv, sizeof(BBOX_AUXV_T))); - if (iReadSize != sizeof(BBOX_AUXV_T)) { - bbox_print(PRINT_ERR, "sys_read is failed, iReadSize = %zd.\n", iReadSize); - BBOX_NOINTR(sys_close(iAuxvFd)); - return RET_ERR; - } - if (sizeof(BBOX_AUXV_T) != BBOX_DoWrite(pstFileFds, &stAuxv, sizeof(BBOX_AUXV_T))) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); - BBOX_NOINTR(sys_close(iAuxvFd)); - return RET_ERR; - } - } - - BBOX_NOINTR(sys_close(iAuxvFd)); - - bbox_print(PRINT_LOG, - "Write Auxv size = %zd to the core file success.\n", - sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + stElfNhdr.n_descsz); - - return RET_OK; -} -/* - * Writes information of the structure that describes the state of a process to the core file - * in : struct BBOX_ELF_PRPSSTATUS *pstPrPsStatusInfo - A pointer to the structure that describes the state - * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file - * return RET_OK or RET_ERR. - */ -static int BBOX_WritePrPsStatusToFile(struct BBOX_ELF_PRPSSTATUS* pstPrPsStatusInfo, struct BBOX_WRITE_FDS* pstFileFds) -{ - BBOX_NHDR stElfNhdr; - int iResult = -1; - - if (NULL == pstPrPsStatusInfo || NULL == pstFileFds) { - bbox_print(PRINT_ERR, - "BBOX_WritePrPsStatusToFile parameters is invalid: pstPrPsStatusInfo or pstFileFds is NULL.\n"); - - return RET_ERR; - } - - errno_t rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); - securec_check_c(rc, "\0", "\0"); - - stElfNhdr.n_namesz = BBOX_CORE_NAME_LENGTH; - stElfNhdr.n_descsz = sizeof(struct BBOX_ELF_PRPSSTATUS); - stElfNhdr.n_type = NT_PRSTATUS; - - if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || - BBOX_DoWrite(pstFileFds, (void*)BBOX_CORE_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); - return RET_ERR; - } - - iResult = BBOX_DoWrite(pstFileFds, pstPrPsStatusInfo, sizeof(struct BBOX_ELF_PRPSSTATUS)); - if (iResult == RET_ERR || sizeof(struct BBOX_ELF_PRPSSTATUS) != iResult) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - - return RET_OK; -} - -/* - * Writes the FPU register contents to the core file - * in : struct BBOX_FPREGSET *pstFpRegisters - A pointer to an FPU register - * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file - * return RET_OK or RET_ERR. - */ -static int BBOX_WriteFpRegistersToFile(struct BBOX_FPREGSET* pstFpRegisters, struct BBOX_WRITE_FDS* pstFileFds) -{ -/* since ptrace() doesn't support to obtain float registers' context in aarch64, don't dump it out. */ -#if !defined(__aarch64__) - BBOX_NHDR stElfNhdr; - - if (NULL == pstFpRegisters || NULL == pstFileFds) { - bbox_print(PRINT_ERR, - "BBOX_WriteFpRegistersToFile parameters is invalid: pstFpRegisters or pstFileFds is NULL.\n"); - return RET_ERR; - } - - errno_t rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); - securec_check_c(rc, "\0", "\0"); - - stElfNhdr.n_namesz = BBOX_CORE_NAME_LENGTH; - stElfNhdr.n_descsz = sizeof(struct BBOX_FPREGSET); - stElfNhdr.n_type = NT_FPREGSET; - - if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || - BBOX_DoWrite(pstFileFds, (void*)BBOX_CORE_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH || - BBOX_DoWrite(pstFileFds, pstFpRegisters, sizeof(struct BBOX_FPREGSET)) != sizeof(struct BBOX_FPREGSET)) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); - return RET_ERR; - } -#endif - - return RET_OK; -} - -/* - * Writes the SSE structure contents to the core file - * in : struct BBOX_FPXREGSET *pstFpxRegisters - A pointer to an SSE structure - * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file - * return RET_OK or RET_ERR. - */ -static int BBOX_WriteFpxRegistersToFile(struct BBOX_FPXREGSET* pstFpxRegisters, struct BBOX_WRITE_FDS* pstFileFds) -{ - BBOX_NHDR stElfNhdr; - - if (NULL == pstFpxRegisters || NULL == pstFileFds) { - bbox_print(PRINT_ERR, - "BBOX_WriteFpxRegistersToFile parameters is invalid: pstFpxRegisters or pstFileFds is NULL.\n"); - return RET_ERR; - } - - errno_t rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); - securec_check_c(rc, "\0", "\0"); - - stElfNhdr.n_namesz = BBOX_LINUX_NAME_LENGTH; - stElfNhdr.n_descsz = sizeof(struct BBOX_FPXREGSET); - stElfNhdr.n_type = NT_PRXFPREG; - - if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || - BBOX_DoWrite(pstFileFds, (void*)BBOX_LINUX_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH || - BBOX_DoWrite(pstFileFds, pstFpxRegisters, sizeof(struct BBOX_FPXREGSET)) != sizeof(struct BBOX_FPXREGSET)) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); - return RET_ERR; - } - - return RET_OK; -} - -/* - * Writes the prpsstatus information and the register information for the process to the core file - * in : int iFpxRegistersFlag - Whether to write Fpx register information token - * struct BBOX_THREAD_NOTE_INFO *pstThreadPrPsStatusInfo - A pointer to the structure of thread note segment - * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file - * return RET_OK or RET_ERR. - */ -static int BBOX_WriteThreadNoteToFile( - int iFpxRegistersFlag, struct BBOX_THREAD_NOTE_INFO* pstThreadPrPsStatusInfo, struct BBOX_WRITE_FDS* pstFileFds) -{ - int iResult = RET_ERR; - - if (NULL == pstThreadPrPsStatusInfo || NULL == pstFileFds) { - bbox_print(PRINT_ERR, - "BBOX_WriteThreadNoteToFile parameters is invalid: pstThreadPrPsStatusInfo or pstFileFds is NULL.\n"); - return RET_ERR; - } - - /* Write CPU register information to core file */ - iResult = BBOX_WritePrPsStatusToFile(&(pstThreadPrPsStatusInfo->stPrpsstatus), pstFileFds); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WritePrPsStatusToFile is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - - /* Write FPU register information to core file */ - iResult = BBOX_WriteFpRegistersToFile(&(pstThreadPrPsStatusInfo->stFpRegisters), pstFileFds); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteFpRegistersToFile is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - - if (BBOX_TRUE == iFpxRegistersFlag) { - /* Write SSE information into core file if exist. */ - iResult = BBOX_WriteFpxRegistersToFile(&(pstThreadPrPsStatusInfo->stFpxRegisters), pstFileFds); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteFpxRegistersToFile is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - } - - return RET_OK; -} - -/* - * To align, write 0 to the core file - * in : size_t uiNoteAlign - The number of bytes that need to be written to the file for alignment - * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file - */ -static int BBOX_CoreFileAlignToPage(size_t uiNoteAlign, struct BBOX_WRITE_FDS* pstFileFds) -{ - ssize_t iWriteSize = -1; - size_t iDateSize = 0; - char acNoteAlign[BBOX_BUFF_LITTLE_SIZE]; - - if (NULL == pstFileFds) { - bbox_print(PRINT_ERR, "BBOX_CoreFileAlignToPage parameters is invalid: pstFileFds is NULL.\n"); - return RET_ERR; - } - - while (uiNoteAlign > 0) { - if (uiNoteAlign > sizeof(acNoteAlign)) { - iDateSize = sizeof(acNoteAlign); - uiNoteAlign -= sizeof(acNoteAlign); - } else { - iDateSize = uiNoteAlign; - uiNoteAlign = 0; - } - - errno_t rc = memset_s(acNoteAlign, BBOX_BUFF_LITTLE_SIZE, 0, sizeof(acNoteAlign)); - securec_check_c(rc, "\0", "\0"); - - iWriteSize = BBOX_DoWrite(pstFileFds, acNoteAlign, iDateSize); - if (iWriteSize == RET_ERR || iDateSize != (size_t)iWriteSize) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iWriteSize = %zd.\n", iWriteSize); - - return RET_ERR; - } - } - - bbox_print(PRINT_LOG, "Write Note align size = %zu to the core file success.\n", uiNoteAlign); - - return RET_OK; -} - -/* - * write Note segment into core file. - * in : struct BBOX_ELF_NOTE_INFO *pstNoteInfo - A pointer to the structure of the note segment - * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file - */ -static int BBOX_WriteNoteInfo(struct BBOX_ELF_NOTE_INFO* pstNoteInfo, struct BBOX_WRITE_FDS* pstFileFds) -{ - unsigned int uCount = 0; - int iResult = RET_ERR; - - if (NULL == pstNoteInfo || NULL == pstFileFds) { - bbox_print(PRINT_ERR, - "BBOX_WriteNoteInfo parameters is invalid: pstNoteInfo or pstFileFds is NULL.\n"); - return RET_ERR; - } - - int iThreadNum = pstNoteInfo->iThreadNoteInfoNum; - struct BBOX_THREAD_NOTE_INFO* pstThreadPrPsStatusInfo = pstNoteInfo->pstThreadNoteInfo; - struct BBOX_ELF_PRPSINFO* pstPrPsInfo = &(pstNoteInfo->stPrpsinfo); - struct BBOX_CORE_USER* pstCoreUserInfo = &(pstNoteInfo->stCoreUser); - - /* write BBOX_ELF_PRPSINFO into core file */ - iResult = BBOX_WritePrPsinfoToFile(pstPrPsInfo, pstFileFds); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WritePrPsinfoToFile is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - - /* write user data into core file */ - iResult = BBOX_WriteUserRegistersToFile(pstCoreUserInfo, pstFileFds); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteUserRegistersToFile is failed, iResult = %d.\n", iResult); - return RET_ERR; - } - - if (pstNoteInfo->iAuxvNoteInfoNum) { - /* write Auxv into core */ - iResult = BBOX_WriteAuxvInfoToFile(pstNoteInfo->iAuxvNoteInfoNum, pstFileFds); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteAuxvInfoToFile is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - } - - for (uCount = 0; uCount < (unsigned int)iThreadNum; uCount++) { - if ((pstThreadPrPsStatusInfo + uCount)->stPrpsstatus.tpid == pstNoteInfo->tMainPid) { - /* write primary process register information into core file. */ - iResult = BBOX_WriteThreadNoteToFile( - pstNoteInfo->iFpxRegistersFlag, (pstThreadPrPsStatusInfo + uCount), pstFileFds); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteThreadNoteToFile is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - } - } - - for (uCount = 0; uCount < (unsigned int)iThreadNum; uCount++) { - if ((pstThreadPrPsStatusInfo + uCount)->stPrpsstatus.tpid != pstNoteInfo->tMainPid) { - /* write all non-primary process register information into core file. */ - iResult = BBOX_WriteThreadNoteToFile( - pstNoteInfo->iFpxRegistersFlag, (pstThreadPrPsStatusInfo + uCount), pstFileFds); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteThreadNoteToFile is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - } - } - - bbox_print(PRINT_LOG, "Write Prpsstatus and Registers to the core file success.\n"); - - /* align the Note segment */ - iResult = BBOX_CoreFileAlignToPage(pstNoteInfo->uiNoteAlign, pstFileFds); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_CoreFileAlignToPage is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - bbox_print(PRINT_LOG, "Write Note info to the core file success.\n"); - - return RET_OK; -} - -/* - * read content of address space, and write it into core file. - * in : struct BBOX_VM_MAPS *pstVmMappingSegment - A pointer to the structure of the mapping segment - * int iVmMappingNum - The number of mapping segments in the address space - * union BBOX_VM_VDSO *pstVmVDSO - A pointer to the structure of the VDSO segment - * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to core file - * return RET_OK or RET_ERR - */ -static int BBOX_WriteElfVmToFile(struct BBOX_VM_MAPS* pstVmMappingSegment, int iVmMappingNum, - union BBOX_VM_VDSO* pstVmVDSO, struct BBOX_WRITE_FDS* pstFileFds) -{ - unsigned int uCount = 0; - size_t uiStartAddress = 0; - size_t uiWriteSize = 0; - ssize_t iResult = RET_ERR; - - if (NULL == pstVmMappingSegment || iVmMappingNum < 0 || NULL == pstFileFds || NULL == pstVmVDSO) { - bbox_print(PRINT_ERR, - "BBOX_WriteElfVmToFile parameters is invalid: iVmMappingNum = %d, " \ - "pstVmMappingSegment, pstFileFds or pstVmVDSO is NULL.\n", - iVmMappingNum); - - return RET_ERR; - } - - /* read content from start address to end address in address space, and write it into core file. */ - for (uCount = 0; uCount < (unsigned int)iVmMappingNum; uCount++) { - uiStartAddress = pstVmMappingSegment[uCount].uiStartAddress; - uiWriteSize = pstVmMappingSegment[uCount].uiWriteSize; - - if (pstVmMappingSegment[uCount].iIsRemoveFlags == BBOX_FALSE && uiWriteSize > 0) { - iResult = BBOX_DoWrite(pstFileFds, (void*)uiStartAddress, uiWriteSize); - if ((iResult == RET_ERR) || (iResult != (ssize_t)uiWriteSize)) { - bbox_print(PRINT_ERR, "BBOX_DoWrite parameters is failed, iWriteSize = %zu.\n", uiWriteSize); - - return RET_ERR; - } - - bbox_print( - PRINT_DBG, "pstVmMappingSegment[%u] : write size = %zu to the core file.\n", uCount, uiWriteSize); - } - } - - if (pstVmVDSO->uiVDSOAddress) { - BBOX_PHDR* pstVDSOPhdr = (BBOX_PHDR*)(pstVmVDSO->uiVDSOAddress + pstVmVDSO->pVDSOEhdr->e_phoff); - for (uCount = 0; uCount < pstVmVDSO->pVDSOEhdr->e_phnum; uCount++) { - /* wirte VDSO information into core file */ - BBOX_PHDR* pstVDSOTempPhdr = pstVDSOPhdr + uCount; - if (PT_LOAD != pstVDSOTempPhdr->p_type) { - iResult = BBOX_DoWrite(pstFileFds, (void*)pstVDSOTempPhdr->p_vaddr, pstVDSOTempPhdr->p_filesz); - if ((iResult == RET_ERR) || (iResult != (ssize_t)(pstVDSOTempPhdr->p_filesz))) { - bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iResult = %zd.\n", iResult); - return RET_ERR; - } - - bbox_print(PRINT_DBG, "VDSO[%u] : write size = %zd to the core file.\n", uCount, iResult); - } - } - } - - bbox_print(PRINT_LOG, "Write Vm mapping segment to the core file success.\n"); - - return RET_OK; -} - -/* - * write all segment into core file, include NOTE, mapping, VDSO - * in : struct BBOX_ELF_NOTE_INFO *pstNoteInfo - A pointer to the structure of the note segment - * struct BBOX_VM_MAPS *pstVmMappingSegment - A pointer to the structure of the mapping segment - * int iSegmentNum - The number of mapping segments in the address space - * union BBOX_VM_VDSO *pstVmVDSO - A pointer to the structure of the VDSO segment - * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to core file - */ -static int BBOX_WriteElfSegment(struct BBOX_ELF_NOTE_INFO* pstNoteInfo, struct BBOX_VM_MAPS* pstVmMappingSegment, - int iSegmentNum, union BBOX_VM_VDSO* pstVmVDSO, struct BBOX_WRITE_FDS* pstFileWriteFd) -{ - int iResult = RET_ERR; - - if (NULL == pstNoteInfo || NULL == pstVmMappingSegment || NULL == pstVmVDSO || NULL == pstFileWriteFd || - iSegmentNum <= 0) { - bbox_print(PRINT_ERR, - "BBOX_WriteElfSegment parameters is invalid: pstNoteInfo, pstVmMappingSegment, " \ - "pstVmVDSO or pstFileWriteFd is NULL, iSegmentNum = %d.\n", - iSegmentNum); - - return RET_ERR; - } - - /* write Note segment into core file */ - iResult = BBOX_WriteNoteInfo(pstNoteInfo, pstFileWriteFd); - if (RET_OK != iResult) { - - bbox_print(PRINT_ERR, "BBOX_WriteNoteInfo is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - /* write process address space information into core file. */ - iResult = BBOX_WriteElfVmToFile(pstVmMappingSegment, iSegmentNum, pstVmVDSO, pstFileWriteFd); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteElfVmToFile is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - return RET_OK; -} - -/* - * calculate end time of coredump and print the time coredump take. - */ -static void BBOX_CalculateUsedTime(void) -{ - long int iCoreDumpEndTime = 0; - struct kernel_timeval stProgramCoreDumpTime = {0}; - - /* calculate time. */ - sys_gettimeofday(&stProgramCoreDumpTime, NULL); - iCoreDumpEndTime = stProgramCoreDumpTime.tv_sec; - - bbox_print(PRINT_TIP, "Coredump probably end at %ld\n", stProgramCoreDumpTime.tv_sec); - bbox_print(PRINT_TIP, "Coredump used time: %ld sec\n", iCoreDumpEndTime - g_iCoreDumpBeginTime); - bbox_print(PRINT_LOG, "Get information success.\n"); - bbox_print(PRINT_LOG, "Create core file success.\n"); -} - -/* - * Write all section into core file. - * in : struct BBOX_ELF_SECTION *pstElfSectionInfo - pointer to the structure of a section - * struct BBOX_WRITE_FDS *pstFileWriteFd - pointer to the core file - * return RET_OK or RET_ERR - */ -static int BBOX_WriteElfSection(struct BBOX_ELF_SECTION* pstElfSectionInfo, struct BBOX_WRITE_FDS* pstFileFds) -{ - unsigned int uiCount = 0; - BBOX_SECTION_STRU* pstSection = NULL; - - /* print time and end information of core file. */ - BBOX_CalculateUsedTime(); - - /* Write section into core file. */ - for (uiCount = 0; uiCount < pstElfSectionInfo->uiSectionNum; uiCount++) { - pstSection = pstElfSectionInfo->pstSection + uiCount; - BBOX_WRITE(pstFileFds, pstSection->pacSectionDesc, pstSection->uiSectionDescSize); - } - - bbox_print(PRINT_LOG, "Write all section to fill successed.\n"); - - return RET_OK; -} - -/* - * close core file - * in : struct BBOX_WRITE_FDS *pstFileWriteFd - Pointer to a structure that describes the properties of a core file. - * return RET_OK or RET_ERR - */ -static int BBOX_CloseCoreFile(struct BBOX_WRITE_FDS* pstFileWriteFd) -{ - if (NULL == pstFileWriteFd) { - bbox_print(PRINT_ERR, "BBOX_CloseCoreFile parameters is invalid: pstFileWriteFd is NULL.\n"); - - return RET_ERR; - } - - if (pstFileWriteFd->iWriteFd >= 0) { - sys_pclose(pstFileWriteFd->iWriteFd); - pstFileWriteFd->iWriteFd = -1; - } - - bbox_print(PRINT_LOG, "Close core file success.\n"); - return RET_OK; -} - -/* - * create elf core file. - * in : BBOX_GetAllThreadDone pDone - The callback function for thawing - * void *pDoneHandle - The callback function parameter to be thawed - * int iNumThreads - The number of processes, that is, the length of the process number array - * pid_t *pPids - pointer to execute the process number array - * va_list ap - Multiparameter list - * return RET_OK or RET_ERR - */ -int BBOX_DoDumpElfCore(BBOX_GetAllThreadDone pDone, void* pDoneHandle, int iNumThreads, pid_t* ptPids, va_list ap) -{ - int iSegmentNum = -1; - int iResult = RET_ERR; - int iCloseFile = RET_ERR; - int iPhdrSum = 0; - pid_t tMainPid = 0; - Frame* pFrame = NULL; - char* pFileName = NULL; - union BBOX_VM_VDSO stVmVDSO; - struct BBOX_ELF_NOTE_INFO stNoteInfo; - struct BBOX_WRITE_FDS stFileWriteFd; - errno_t rc = EOK; - - if (NULL == ptPids || NULL == pDone || NULL == pDoneHandle || iNumThreads <= 0) { - bbox_print(PRINT_ERR, - "BBOX_CloseCoreFile parameters is invalid: pDone, ptPids or pDoneHandle is NULL, " \ - "iNumThreads = %d.\n", - iNumThreads); - - return RET_ERR; - } - - /* Atomic variable lock to prevent reentry. */ - while (BBOX_AtomicIncReturn(&g_stLockBlackList) > 1) { - BBOX_AtomicDec(&g_stLockBlackList); - bbox_print(PRINT_DBG, "add blacklist addr is running, waiting.\n"); - sleep(1); - } - - struct BBOX_THREAD_NOTE_INFO astThreadNoteInfo[iNumThreads]; - - pFrame = (Frame*)va_arg(ap, Frame*); - if (NULL == pFrame) { - bbox_print(PRINT_ERR, "Get stack frame failed.\n"); - BBOX_AtomicDec(&g_stLockBlackList); - return RET_ERR; - } - - rc = memset_s(&stVmVDSO, sizeof(union BBOX_VM_VDSO), 0, sizeof(union BBOX_VM_VDSO)); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(&stNoteInfo, sizeof(struct BBOX_ELF_NOTE_INFO), 0, sizeof(struct BBOX_ELF_NOTE_INFO)); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(&stFileWriteFd, sizeof(struct BBOX_WRITE_FDS), 0, sizeof(struct BBOX_WRITE_FDS)); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(astThreadNoteInfo, - iNumThreads * sizeof(struct BBOX_THREAD_NOTE_INFO), - 0, - iNumThreads * sizeof(struct BBOX_THREAD_NOTE_INFO)); - securec_check_c(rc, "\0", "\0"); - - stNoteInfo.pstThreadNoteInfo = astThreadNoteInfo; - stNoteInfo.iThreadNoteInfoNum = iNumThreads; /* count of thread */ - stVmVDSO.uiVDSOAddress = 0; - tMainPid = pFrame->tid; - stNoteInfo.tMainPid = tMainPid; - - /* Get count of line in /proc/self/maps, it also is segment num. */ - iSegmentNum = BBOX_GetVmMapsNum(); - if (iSegmentNum <= 0) { - bbox_print(PRINT_ERR, "BBOX_GetVmMapsNum is invald iSegmentNum = %d\n", iSegmentNum); - BBOX_AtomicDec(&g_stLockBlackList); - return RET_ERR; - } - - bbox_print(PRINT_LOG, "Get Vm mapping number success, iSegmentNum = %d\n", iSegmentNum); - - /* define variable to record start address, end address, jurisdiction, offset and so on of segment in /proc/self/maps */ - struct BBOX_VM_MAPS astVmMappingSegment[iSegmentNum + BBOX_EXTERN_VM_MAX]; - - rc = memset_s(astVmMappingSegment, - (iSegmentNum + BBOX_EXTERN_VM_MAX) * sizeof(struct BBOX_VM_MAPS), - 0, - (iSegmentNum + BBOX_EXTERN_VM_MAX) * sizeof(struct BBOX_VM_MAPS)); - securec_check_c(rc, "\0", "\0"); - - /* Fill BBOX_VM_MAPS, BBOX_ELF_NOTE_INFO, BBOX_VM_VDSO, and write them into core file. */ - iResult = BBOX_FillAllInfoOfCoreFile( - pFrame, astVmMappingSegment, &iSegmentNum, &iPhdrSum, ptPids, &stNoteInfo, &stVmVDSO); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_FillAllInfoOfCoreFile is failed, iResult = %d.\n", iResult); - BBOX_AtomicDec(&g_stLockBlackList); - return RET_ERR; - } - - /* Notifie the thread module that the data retrieval is complete. */ - pDone(pDoneHandle); - - pFileName = (char*)va_arg(ap, char*); - if (CheckFilenameValid(pFileName) == RET_ERR) { - bbox_print(PRINT_ERR, "check core file name failed\n"); - return RET_ERR; - } - iResult = BBOX_OpenCoreFile(pFrame, pFileName, &stFileWriteFd); /* create core file. */ - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_OpenCoreFile is failed, iResult = %d.\n", iResult); - - goto ERR; - } - - /* fill core file header and write it into core file. */ - iResult = BBOX_WriteElfEhdr(iPhdrSum, &stFileWriteFd); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteElfEhdr is failed, iResult = %d.\n", iResult); - goto ERR; - } - - /* Fill ElfPhdr and write it into core file. */ - iResult = BBOX_WriteElfHdr(iPhdrSum, &stNoteInfo, iSegmentNum, &stVmVDSO, astVmMappingSegment, &stFileWriteFd); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteElfPhdr is failed, iResult = %d.\n", iResult); - goto ERR; - } - - /* Write Note segment and process address content into core file. */ - iResult = BBOX_WriteElfSegment(&stNoteInfo, astVmMappingSegment, iSegmentNum, &stVmVDSO, &stFileWriteFd); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteElfSegment is failed, iResult = %d.\n", iResult); - - goto ERR; - } - - /* Write BBOX_ELF_PRPSINFO into core file */ - iResult = BBOX_WriteElfSection(&g_stElfSectionInfo, &stFileWriteFd); - if (RET_OK != iResult) { - bbox_print(PRINT_ERR, "BBOX_WriteElfSection is failed, iResult = %d.\n", iResult); - - goto ERR; - } - - bbox_print(PRINT_LOG, "Create core file success.\n"); - -ERR: - BBOX_AtomicDec(&g_stLockBlackList); - - iCloseFile = BBOX_CloseCoreFile(&stFileWriteFd); - if (RET_OK != iCloseFile) { - bbox_print(PRINT_ERR, "BBOX_CloseCoreFile is failed, iCloseFile = %d.\n", iCloseFile); - } - - return (iCloseFile == RET_OK) ? iResult : RET_ERR; -} - -#ifdef __cplusplus -#if __cplusplus -} -#endif -#endif /* __cplusplus */ -- 2.34.1 From e338a73024c69bc3ffe23547bfe17259d48edde8 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:17:30 +0800 Subject: [PATCH 21/56] ADD file via upload --- src/gausskernel/cbb/bbox/bbox_elf_dump.cpp | 2962 ++++++++++++++++++++ 1 file changed, 2962 insertions(+) create mode 100644 src/gausskernel/cbb/bbox/bbox_elf_dump.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_elf_dump.cpp b/src/gausskernel/cbb/bbox/bbox_elf_dump.cpp new file mode 100644 index 000000000..b1f1603a4 --- /dev/null +++ b/src/gausskernel/cbb/bbox/bbox_elf_dump.cpp @@ -0,0 +1,2962 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * bbox_elf_dump.cpp + * + * IDENTIFICATION + * src/gausskernel/cbb/bbox/bbox_elf_dump.cpp + * + * ------------------------------------------------------------------------- + */ +#include "bbox_elf_dump.h" +#include "bbox_syscall_support.h" +#include "postgres.h" +#include "gs_bbox.h" +#include "../../src/include/securec.h" +#include "../../src/include/securec_check.h" + +#if UINTPTR_MAX == 0xffffffff +#define Elf_Ehdr Elf32_Ehdr +#else +#define Elf_Ehdr Elf64_Ehdr +#endif + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +extern long int g_iCoreDumpBeginTime; /* begin time of coredump */ + +struct BBOX_ELF_SECTION g_stElfSectionInfo; /* information of all section. */ +BBOX_SECTION_STRU g_stSectionInfo[BBOX_SECTION_NUM]; /* array to record section information. */ +char g_acBboxAddonInfo[BBOX_ADDON_INFO_SIZE]; /* record system information. */ +char g_acBboxStrTabInfo[BBOX_SH_STR_TAB_SIZE]; /* record string symbol table. */ + +/* + * ignore the field that discribe equipment or node when analyze a line of /proc/self/maps. + * in : struct BBOX_READ_FILE_IO *pstReadIO - file pointer to be read + * return : iGetChar - current character of the file being read + * RET_ERR - failed + */ + +static int BBOX_SkipDeviceAndNodeField(struct BBOX_READ_FILE_IO* pstReadIO) +{ + int iCount = -1; + int iGetChar = -1; + + // 检查参数的有效性 +if (NULL == pstReadIO) { + bbox_print(PRINT_ERR, "BBOX_SkipDeviceAndNodeField参数无效:pstReadIO为NULL。\n"); + return RET_ERR; +} + +// 从文件中获取一个字符 +iGetChar = BBOX_GetCharFromFile(pstReadIO); +if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,iGetChar= %d。\n", iGetChar); + return RET_ERR; +} + +// 进入循环,跳过设备和节点字段 +for (iCount = 0; iCount < DEVICE_AND_NODE_FIELD_NUM; iCount++) { + // 跳过空格字符 + while (iGetChar == ' ') { + // 继续从文件中获取下一个字符 + iGetChar = BBOX_GetCharFromFile(pstReadIO); + if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,iGetChar= %d。\n", iGetChar); + return RET_ERR; + } + } + + // 跳过非空格和换行符的字符 + while (iGetChar != ' ' && iGetChar != '\n') { + if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,iGetChar= %d。\n", iGetChar); + return RET_ERR; + } + // 继续从文件中获取下一个字符 + iGetChar = BBOX_GetCharFromFile(pstReadIO); + } + + // 跳过空格字符 + while (iGetChar == ' ') { + // 继续从文件中获取下一个字符 + iGetChar = BBOX_GetCharFromFile(pstReadIO); + if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,iGetChar= %d。\n", iGetChar); + return RET_ERR; + } + } +} + +return iGetChar; // 返回读取的下一个字符 +} + +/* + * set whether the mapping is labeled as PF_DEVICE, means that, whether or not it's a device mapping. + * in : int *piGetChar - pointer to the character read + * struct BBOX_READ_FILE_IO *pstReadIO - pointer to struct of file read + * struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to discription of mapping segment structure. + * return RET_OK or RET_ERR + */ +static int BBOX_SetMappingDeviceFlag( + int* piGetChar, struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) +{ + int iIsMappingDevicesFlag = BBOX_FALSE; + const char* pszDeviceZero = DEVICE_ZERO_NAME_STRING; + const char* pszDevice = pszDeviceZero; + + if (NULL == piGetChar || NULL == pstReadIO || NULL == pstSegmentMapping) { + + bbox_print(PRINT_ERR, + "BBOX_FillMappingFlagsAndOffset parameters is invalid: " \ + "piGetChar, pstReadIO or pstSegmentMapping is NULL.\n"); + + return RET_ERR; + } + + /* compare and determine if it is a device */ + while (*pszDevice && *piGetChar == *pszDevice) { + *piGetChar = BBOX_GetCharFromFile(pstReadIO); + if (RET_ERR == *piGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, *piGetChar= %d.\n", *piGetChar); + return RET_ERR; + } + + pszDevice++; + } + + iIsMappingDevicesFlag = (pszDevice >= pszDeviceZero + DEVICE_PREFIX_LEN) && + ((*piGetChar != '\n' && *piGetChar != ' ') || *pszDevice != '\000'); + if (BBOX_TRUE == iIsMappingDevicesFlag) { + pstSegmentMapping->iFlags |= PF_DEVICE; /* set flag of equipment. */ + + bbox_print(PRINT_DBG, + " Get Device Segment: StartAddr = %zu, EndAddr = %zu.\n", + pstSegmentMapping->uiStartAddress, + pstSegmentMapping->uiEndAddress); + } + + return RET_OK; +} + + /* +这段代码是一个名为BBOX_SetMappingVDSOFlag的静态函数。 +该函数用于设置映射的VDSO标志以及VVAR标志。 +参数: +piGetChar: 指向整型变量的指针,用于获取从文件中读取的字符 +pstReadIO: 一个指向BBOX_READ_FILE_IO结构的指针,用于读取文件 +pstSegmentMapping: 一个指向BBOX_VM_MAPS结构的指针,表示虚拟内存映射段的信息 +返回值: +成功:返回RET_OK +失败:返回RET_ERR */ +static int BBOX_SetMappingVDSOFlag(int* piGetChar, struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) +{ +int iIsMappingVdsoFlag = BBOX_FALSE; // 表示是否映射了VDSO的标志,初始为假 +int iIsMappingVvarFlag = BBOX_TRUE; // 表示是否映射了VVAR的标志,初始为真 +const char* pszVdso = VDSO_NAME_STRING; // VDSO名称字符串 +const char* pszVvar = VVAR_NAME_STRING; // VVAR名称字符串 + // 检查参数的有效性 +if (NULL == piGetChar || NULL == pstReadIO || NULL == pstSegmentMapping) { + bbox_print(PRINT_ERR, + "BBOX_SetMappingVDSOFlag参数无效:piGetChar,pstReadIO或pstSegmentMapping为NULL。\n"); + return RET_ERR; +} + +// 当VDSO名称字符串和从文件中获取的字符相等时,进行以下操作 +while (*pszVdso && *piGetChar == *pszVdso) { + // 如果映射了VVAR并且从文件中获取的字符与VVAR名称字符串相等 + if (iIsMappingVvarFlag == BBOX_TRUE) { + iIsMappingVvarFlag = (*piGetChar == *pszVvar) ? BBOX_TRUE : BBOX_FALSE; + pszVvar++; + } + + // 继续从文件中获取下一个字符 + *piGetChar = BBOX_GetCharFromFile(pstReadIO); + if (RET_ERR == *piGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,*piGetChar= %d。\n", *piGetChar); + return RET_ERR; + } + + pszVdso++; +} + +// 如果VDSO名称字符串为空,且从文件中获取的字符为换行符、空格或空字符 +if (*pszVdso == '\0' && (*piGetChar == '\n' || *piGetChar == ' ' || *piGetChar == '\0')) { + pstSegmentMapping->iFlags |= PF_VDSO; // 设置VDSO标志 + + bbox_print(PRINT_DBG, + "获取VDSO的起始地址 = %zu,结束地址 = %zu。\n", + pstSegmentMapping->uiStartAddress, + pstSegmentMapping->uiEndAddress); +} + +// 如果映射了VVAR,并且从文件中获取的字符与VVAR名称字符串相等时,进行以下操作 +if (iIsMappingVvarFlag == BBOX_TRUE) { + while (*pszVdso && *piGetChar == *pszVvar) { + // 继续从文件中获取下一个字符 + *piGetChar = BBOX_GetCharFromFile(pstReadIO); + if (RET_ERR == *piGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile调用失败,*piGetChar= %d。\n", *piGetChar); + return RET_ERR; + } + + pszVvar++; + } + + // 如果VVAR名称字符串为空,且从文件中获取的字符为换行符、空格或空字符 + if (*pszVvar == '\0' && (*piGetChar == '\n' || *piGetChar == ' ' || *piGetChar == '\0')) { + pstSegmentMapping->iFlags |= PF_VVAR; // 设置VVAR标志 + + bbox_print(PRINT_DBG, + "获取VVAR的起始地址 = %zu,结束地址 = %zu。\n", + pstSegmentMapping->uiStartAddress, pstSegmentMapping->uiEndAddress); + } +} + +return RET_OK; +} +/* + * check if the file is a dynamic library file. + * in : char* pszFilePath - path + * struct BBOX_VM_MAPS *pstSegmentMapping - pointer to discription of mapping segment structuer. + * return RET_OK or RET_ERR. + */ +static int BBOX_SettingMappedFileFlag(char* pszFilePath, struct BBOX_VM_MAPS* pstSegmentMapping) +{ + int fd = -1; + int retval = -1; + Elf_Ehdr ehdr; + + fd = sys_open(pszFilePath, O_RDONLY, 0); + if (fd < 0) { + bbox_print(PRINT_ERR, "open file %s failed, errno = %d\n", pszFilePath, errno); + return RET_ERR; + } + + /* read ELF header */ + retval = sys_read(fd, &ehdr, sizeof(ehdr)); + if (retval < 0) { + bbox_print(PRINT_ERR, "read elf failed, errno = %d\n", errno); + goto errout; + } + + /* check if the file is ELF file. */ + if (bbox_strncmp(ELFMAG, (char*)ehdr.e_ident, SELFMAG)) { + pstSegmentMapping->iFlags |= PF_MAPPEDFILE; + bbox_print(PRINT_DBG, "not an elf file.\n"); + /* this is map file instead of dynamic library file. */ + bbox_print(PRINT_DBG, "mapped file : %s\n", pszFilePath); + } + + sys_close(fd); + return RET_OK; + +errout: + + if (fd > 0) { + sys_close(fd); + } + return RET_ERR; +} + +/* + * check if the file is equipment file. + * in : char* pszFilePath - path + * struct BBOX_VM_MAPS *pstSegmentMapping - pointer to discription of mapping segment structuer. + * return RET_OK or RET_ERR. + */ +static int BBOX_SettingDeviceFileFlag(char* pszFilePath, struct BBOX_VM_MAPS* pstSegmentMapping) +{ + struct kernel_stat stMarkerSB = {0}; + if (sys_stat(pszFilePath, &stMarkerSB) < 0) { + bbox_print(PRINT_ERR, "sys_stat error, errno = %d, path = %s\n", errno, pszFilePath); + return RET_ERR; + } + + if (S_ISCHR(stMarkerSB.st_mode) || S_ISBLK(stMarkerSB.st_mode) || S_ISFIFO(stMarkerSB.st_mode) || + S_ISSOCK(stMarkerSB.st_mode)) { + bbox_print(PRINT_DBG, "Device file : %s\n", pszFilePath); + pstSegmentMapping->iFlags |= PF_DEVICE; + return RET_OK; + } + + return RET_ERR; +} + +/* + * check if corresponding address is a general file mapping by mmap instead of a dynamic library file + * when read /proc/self/maps. + * in : struct BBOX_READ_FILE_IO *pstReadIO - pointer to file to be read. + * struct BBOX_VM_MAPS *pstSegmentMapping - pointer to discription of mapping segment structuer. + * return : int iGetChar - current character of file reading + * RET_ERR - read failed + */ +static int BBOX_SettingFileFlags( + int* iGetChar, struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) +{ + char MapFilePath[PATH_MAX]; + int i = 0; + + MapFilePath[0] = *iGetChar; + i++; + + /* read mapping file path after reading all address. */ + while ((*iGetChar = BBOX_GetCharFromFile(pstReadIO)) != '\n' && i < PATH_MAX - 1) { + if (RET_ERR == *iGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", *iGetChar); + return RET_ERR; + } + + MapFilePath[i] = *iGetChar; + i++; + } + + MapFilePath[i] = 0; + + if (BBOX_SettingDeviceFileFlag(MapFilePath, pstSegmentMapping) == RET_OK) { + return RET_OK; + } + + if (BBOX_SettingMappedFileFlag(MapFilePath, pstSegmentMapping) == RET_OK) { + return RET_OK; + } + + return RET_ERR; +} + +/* + * fill flag and offset of struct BBOX_VM_MAPS when reading /proc/self/maps. + * in : struct BBOX_READ_FILE_IO *pstReadIO - pointer to file to be read. + * struct BBOX_VM_MAPS *pstSegmentMapping - pointer to discription of mapping segment structuer. + * return : int iGetChar - current character of file reading + * RET_ERR - read failed + */ + /* +这个函数名为BBOX_FillMappingFlagsAndOffset。它的作用是填充虚拟内存映射段的标志位和偏移量。 + +参数: +- pstReadIO: 指向BBOX_READ_FILE_IO结构的指针,用于读取文件 +- pstSegmentMapping: 指向BBOX_VM_MAPS结构的指针,表示虚拟内存映射段的信息 + +返回值: +- 成功: 返回从文件中获取的字符 +- 失败: 返回RET_ERR + +1. 检查参数的有效性,如果传入的指针为空,则打印错误消息并返回RET_ERR。 +2. 通过循环读取标志位,并在读取完地址之后将'-'设置为0。读取的字符通过位运算合并到pstSegmentMapping->iFlags中。 +3. 对pstSegmentMapping->iFlags进行处理,将其右移一位并与PF_MASK进行与运算。 +4. 通过BBOX_StringSwitchInt函数读取偏移量,并将其保存到pstSegmentMapping->uiOffset中。 +5. 跳过描述设备和节点的字段。 +6. 判断下一个字段是否以'['开头或者是结束,如果是,则将其标记为匿名设备。 +7. 如果是匿名设备,设置PF_ANONYMOUS标志,并调用BBOX_SetMappingVDSOFlag函数判断是否是VDSO段,如果是则标记为VDSO并返回获取的字符。 +8. 如果不是匿名设备,则调用BBOX_SetMappingDeviceFlag函数判断是否是描述某个设备的字段,如果是则设置设备标志。 +9. 判断是否存在映射文件,如果存在,则调用BBOX_SettingFileFlags函数设置文件标志。 +10. 返回获取的字符。 + */ +static int BBOX_FillMappingFlagsAndOffset(struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) +{ + int iRessult = 0; + int iGetChar = -1; + int iIsMappingAnonymousFlag = BBOX_FALSE; + int iIsMappedFile = BBOX_FALSE; + + if (NULL == pstReadIO || NULL == pstSegmentMapping) { + bbox_print(PRINT_ERR, + "BBOX_FillMappingFlagsAndOffset parameters is invalid: pstReadIO or " \ + "pstSegmentMapping is NULL.\n"); + return RET_ERR; + } + + /* read flags and set '-' to 0 after reading all address. */ + while ((iGetChar = BBOX_GetCharFromFile(pstReadIO)) != ' ') { + if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile is failed, iGetChar= %d.\n", iGetChar); + return RET_ERR; + } + + pstSegmentMapping->iFlags = (pstSegmentMapping->iFlags << 1) | (unsigned int)(iGetChar != '-'); + } + + pstSegmentMapping->iFlags = ((pstSegmentMapping->iFlags) >> 1) & PF_MASK; + + /* read offset */ + iGetChar = BBOX_StringSwitchInt(pstReadIO, &(pstSegmentMapping->uiOffset)); + if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_StringSwitchInt is failed, iGetChar= %d.\n", iGetChar); + return RET_ERR; + } + + /* ignore the feild that discribe equipment and node which are not needed. */ + iGetChar = BBOX_SkipDeviceAndNodeField(pstReadIO); + if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_SkipDeviceAndNodeField is failed, iGetChar= %d.\n", iGetChar); + return RET_ERR; + } + + /* judge the next field is start with '[' or is end, if yes, mark it as a anonymity equipment. */ + iIsMappingAnonymousFlag = ((iGetChar == '\n') || (iGetChar == '[')); + if (BBOX_TRUE == iIsMappingAnonymousFlag) { + pstSegmentMapping->iFlags |= PF_ANONYMOUS; + /* judge where it is VDSO segment, if yes, mark it. */ + iRessult = BBOX_SetMappingVDSOFlag(&iGetChar, pstReadIO, pstSegmentMapping); + if (RET_OK != iRessult) { + bbox_print(PRINT_ERR, "BBOX_SetMappingVDSOFlag is failed, iRessult= %d.\n", iRessult); + return RET_ERR; + } + + return iGetChar; + } + + /* judge if it is discribing someone equipment. */ + iRessult = BBOX_SetMappingDeviceFlag(&iGetChar, pstReadIO, pstSegmentMapping); + if (RET_OK != iRessult) { + bbox_print(PRINT_ERR, "BBOX_SetMappingDeviceFlag is failed, iRessult= %d.\n", iRessult); + return RET_ERR; + } + + /* judge if it has mapping file. */ + iIsMappedFile = (iGetChar == '/'); + + if (BBOX_TRUE == iIsMappedFile) { + iRessult = BBOX_SettingFileFlags(&iGetChar, pstReadIO, pstSegmentMapping); + if (RET_OK != iRessult) { + bbox_print(PRINT_ERR, "BBOX_SettingFileFlags is failed, iRessult= %d.\n", iRessult); + return RET_ERR; + } + } + + return iGetChar; +} + +/* + * read /proc/self/maps and get count of line in which. + * return : iLineNum - line count of /proc/self/maps, it is not a negative. + * RET_ERR + */ +static int BBOX_GetVmMapsNum(void) +{ + int iFd = -1; + int iLineNum = 0; + int iCount = 0; + ssize_t iReadSize = -1; + char acBuff[BBOX_BUFF_LITTLE_SIZE]; + errno_t rc = EOK; + BBOX_NOINTR(iFd = sys_open(THREAD_SELF_MAPS_FILE, O_RDONLY, 0)); + if (iFd < 0) { + bbox_print(PRINT_ERR, "sys_open is failed, iFd= %d.\n", iFd); + return RET_ERR; + } + + bbox_print(PRINT_DBG, "Read file : /proc/self/Maps:\n"); + + do { + iReadSize = -1; + rc = memset_s(acBuff, BBOX_BUFF_LITTLE_SIZE, 0, sizeof(acBuff)); + securec_check_c(rc, "\0", "\0"); + + BBOX_NOINTR(iReadSize = sys_read(iFd, acBuff, sizeof(acBuff))); + if (RET_ERR == iReadSize) { + BBOX_NOINTR(sys_close(iFd)); + return RET_ERR; + } else if (0 == iReadSize) { + break; + } + + bbox_print(PRINT_DBG, "%s", acBuff); + + for (iCount = 0; iCount < iReadSize; iCount++) { + if ('\n' == acBuff[iCount]) { + iLineNum++; + } + } + } while (iReadSize); + + BBOX_NOINTR(sys_close(iFd)); + + return iLineNum ? iLineNum : RET_ERR; +} + +/* + * fill address feild in struct BBOX_VM_MAPS when reading /proc/self/maps. + * in : struct BBOX_READ_FILE_IO *pstReadIO - pointer to file to be read. + * struct BBOX_VM_MAPS *pstSegmentMapping - pointer to discription of mapping segment structuer. + * return : int iGetChar - current character of file reading + * RET_ERR - read failed + */ +static char BBOX_FillMappingAddress(struct BBOX_READ_FILE_IO* pstReadIO, struct BBOX_VM_MAPS* pstSegmentMapping) +{ + int iGetChar = -1; + + if (NULL == pstReadIO || NULL == pstSegmentMapping) { + bbox_print(PRINT_ERR, + "BBOX_FillMappingAddress parameters is invalid: pstReadIO or pstSegmentMapping is NULL.\n"); + return RET_ERR; + } + + /* read characters from file and convert it to interger until get '-'. + This is start address and then ready to read end address. */ + iGetChar = BBOX_StringSwitchInt(pstReadIO, &(pstSegmentMapping->uiStartAddress)); + if ('-' == iGetChar) { + /* read characters from file and convert it to interger until get '-'. This is end address. */ + iGetChar = BBOX_StringSwitchInt(pstReadIO, &(pstSegmentMapping->uiEndAddress)); + if (' ' != iGetChar) { + bbox_print(PRINT_ERR, "BBOX_StringSwitchInt is failed, iGetChar= %d.\n", iGetChar); + + return RET_ERR; + } + } else { + + bbox_print(PRINT_ERR, "BBOX_StringSwitchInt is failed, iGetChar= %d.\n", iGetChar); + + return RET_ERR; + } + + return (char)iGetChar; +} + +/* + * use blacklist items to split the segment, thus drop partal slice from core file. + * struct BBOX_WRITE_FDS *pstWriteFds : segment to be written into core file + * return RET_OK if success else RET_ERR. + */ + /* + 这个函数名为BBOX_VmExecludeBlackList。它的作用是根据黑名单排除虚拟内存映射段。 + +参数: +- pstVmMappingSegment: 指向BBOX_VM_MAPS结构的指针,表示虚拟内存映射段的信息 + +返回值: +- 成功: 返回RET_OK + +1. 定义一些变量并初始化。 +2. 调用_BBOX_FindAddrInBlackList函数在黑名单中查找起始地址和结束地址。 +3. 如果找到了匹配的黑名单节点,则获取黑名单节点的起始地址和结束地址。 +4. 获取下一个虚拟内存映射段的指针。 +5. 将下一个虚拟内存映射段的标志位设置为当前映射段的标志位。 +6. 如果起始地址小于黑名单的起始地址,则更新当前映射段的写入大小、结束地址、起始地址和删除标志。 +7. 否则,将当前映射段的删除标志设置为真。 +8. 如果黑名单的结束地址小于结束地址,则更新下一个虚拟内存映射段的写入大小、起始地址、结束地址和删除标志。 +9. 否则,将下一个虚拟内存映射段的删除标志设置为真。 +10. 如果没有找到匹配的黑名单节点,则什么都不做。 +11. 打印调试消息,并返回RET_OK。 + + */ +static int BBOX_VmExecludeBlackList(struct BBOX_VM_MAPS *pstVmMappingSegment) +{ + void *pStartAddress = (void *)(uintptr_t)pstVmMappingSegment->uiStartAddress; + void *pEndAddress = (void *)(uintptr_t)pstVmMappingSegment->uiEndAddress; + void *pBlackStartAddress = NULL; + void *pBlackEndAddress = NULL; + size_t uiPageSize = sys_sysconf(_SC_PAGESIZE); + BBOX_BLACKLIST_STRU *pstBlackNode = NULL; + struct BBOX_VM_MAPS *pstNextVmSegment = NULL; + + pstBlackNode = _BBOX_FindAddrInBlackList(pStartAddress, pEndAddress); + if (pstBlackNode != NULL) { + + pBlackStartAddress = pstBlackNode->pBlackStartAddr; + pBlackEndAddress = pstBlackNode->pBlackEndAddr; + + pstNextVmSegment = pstVmMappingSegment + 1; + pstNextVmSegment->iFlags = pstVmMappingSegment->iFlags; + + if (pStartAddress < pBlackStartAddress) { + pstVmMappingSegment->uiWriteSize= (size_t)((uintptr_t)pBlackStartAddress - (uintptr_t)pStartAddress); + if ((pstVmMappingSegment->uiWriteSize) < uiPageSize) { + pstVmMappingSegment->uiEndAddress = + (size_t)((uintptr_t)pBlackStartAddress + (pstVmMappingSegment->uiWriteSize % uiPageSize)); + } else { + pstVmMappingSegment->uiEndAddress = (size_t)(uintptr_t)pBlackStartAddress; + } + + pstVmMappingSegment->uiWriteSize= (size_t)((uintptr_t)pBlackStartAddress - (uintptr_t)pStartAddress); + pstVmMappingSegment->uiStartAddress = (size_t)(uintptr_t)pStartAddress; + pstVmMappingSegment->iIsRemoveFlags = BBOX_FALSE; + } else { + pstVmMappingSegment->iIsRemoveFlags = BBOX_TRUE; + } + + if (pBlackEndAddress < pEndAddress) { + pstNextVmSegment->uiWriteSize = (size_t)((uintptr_t)pEndAddress - (uintptr_t)pBlackEndAddress); + if ((pstNextVmSegment->uiWriteSize) < uiPageSize) { + pstNextVmSegment->uiStartAddress = + (size_t)((uintptr_t)pBlackEndAddress - (pstNextVmSegment->uiWriteSize % uiPageSize)); + } else { + pstNextVmSegment->uiStartAddress = (size_t)(uintptr_t)pBlackEndAddress; + } + + pstNextVmSegment->uiWriteSize = (size_t)((uintptr_t)pEndAddress - (uintptr_t)pBlackEndAddress); + pstNextVmSegment->uiEndAddress = (size_t)(uintptr_t)pEndAddress; + pstNextVmSegment->iIsRemoveFlags = BBOX_FALSE; + } else { + pstNextVmSegment->iIsRemoveFlags = BBOX_TRUE; + } + } else { + /* do nothing */ + } + + bbox_print(PRINT_DBG, "execlude black list success.\n"); + return RET_OK; +} + +/* + * read /proc/self/maps and fill struct BBOX_VM_MAPS + * in : struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to discription of mapping segment structure. + * int iSegmentNum - count of mapping segment in address space. + * return RET_OK or RET_ERR. + */ +static int BBOX_GetMappingSegment(struct BBOX_VM_MAPS* pstSegmentMapping, int* piSegmentNum) +{ + int iGetChar = -1; + int iResult = RET_ERR; + int iNewVmTotal = 0; + int iVmTotal = 0; + struct BBOX_READ_FILE_IO stReadIO; + struct BBOX_VM_MAPS* pstCurSegment = NULL; + errno_t rc = EOK; + if (NULL == pstSegmentMapping || NULL == piSegmentNum) { + bbox_print(PRINT_ERR, + "BBOX_GetMappingSegment parameters is invalid: " \ + "pstSegmentMapping or piSegmentNum is NULL.\n"); + return RET_ERR; + } + + iVmTotal = *piSegmentNum; + + rc = memset_s(&stReadIO, sizeof(struct BBOX_READ_FILE_IO), 0, sizeof(struct BBOX_READ_FILE_IO)); + securec_check_c(rc, "\0", "\0"); + + stReadIO.iFd = -1; + stReadIO.pData = NULL; + stReadIO.pEnd = NULL; + + BBOX_NOINTR(stReadIO.iFd = sys_open(THREAD_SELF_MAPS_FILE, O_RDONLY, 0)); + if (stReadIO.iFd < 0) { + bbox_print(PRINT_ERR, "sys_open is failed, stReadIO.iFd = %d.\n", stReadIO.iFd); + return RET_ERR; + } + + pstCurSegment = pstSegmentMapping; + while (iVmTotal) { + bbox_print(PRINT_DBG, "iVmTotal = %d.\n", iVmTotal); + + /* get start address and end address of every segment in process address space. */ + iGetChar = BBOX_FillMappingAddress(&stReadIO, pstCurSegment); + if (RET_ERR == iGetChar) { + BBOX_NOINTR(sys_close(stReadIO.iFd)); + bbox_print(PRINT_ERR, "BBOX_FillMappingAddress is failed, iGetChar = %d.\n", iGetChar); + return RET_ERR; + } + + /* get jurisdiction flag and offset of every segment in process address space. */ + iGetChar = BBOX_FillMappingFlagsAndOffset(&stReadIO, pstCurSegment); + if (RET_ERR == iGetChar) { + BBOX_NOINTR(sys_close(stReadIO.iFd)); + bbox_print(PRINT_ERR, "BBOX_FillMappingFlagsAndOffset is failed, iGetChar = %d.\n", iGetChar); + return RET_ERR; + } + + if (iGetChar != '\n') { + /* ignore other information and skip to the end of line for preparing to read next line. */ + iResult = BBOX_SkipToLineEnd(&stReadIO); + if (RET_OK != iResult) { + BBOX_NOINTR(sys_close(stReadIO.iFd)); + bbox_print(PRINT_ERR, "BBOX_SkipToLineEnd is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + } + + while (_BBOX_FindAddrInBlackList( + (void *)(uintptr_t)pstCurSegment->uiStartAddress, (void *)(uintptr_t)pstCurSegment->uiEndAddress)) { + + iResult = BBOX_VmExecludeBlackList(pstCurSegment); + if (RET_OK != iResult) { + BBOX_NOINTR(sys_close(stReadIO.iFd)); + bbox_print(PRINT_ERR, "BBOX_VmExecludeBlackList is failed.\n"); + return RET_ERR; + } + + iNewVmTotal++; + pstCurSegment++; + } + + pstCurSegment++; + iVmTotal--; + } + + (*piSegmentNum) = (*piSegmentNum) + iNewVmTotal; + + bbox_print(PRINT_TIP, "after BL, all segment num is %d.\n", *piSegmentNum); + + BBOX_NOINTR(sys_close(stReadIO.iFd)); + + return RET_OK; +} + +/* + * judge if it should be written into core file for every segment in address space, + * and calculate the size to be written in. + * in : struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to mapping segment structure. + * int iSegmentNum - count of mapping segment in address space + * int *piValidSegmentNum - count of valid mapping segment in address space, + * which need to be written into core file. + * return RET_OK or RET_ERR. + */ +static int BBOX_VmMappingSizeDump(struct BBOX_VM_MAPS* pstVmMappingSegment, int iSegmentNum, int* piValidSegmentNum) +{ + int iCount = 0; + struct BBOX_VM_MAPS* pstVmMapping = NULL; + + if (NULL == pstVmMappingSegment || NULL == piValidSegmentNum || iSegmentNum <= 0) { + bbox_print(PRINT_ERR, + "BBOX_VmMappingSizeDump parameters is invalid: pstVmMappingSegment, " \ + "piValidSegmentNum or iSegmentNum is NULL.\n"); + + return RET_ERR; + } + + *piValidSegmentNum = iSegmentNum; + + bbox_print(PRINT_LOG, "write segment to core file:\n"); + + for (iCount = 0; iCount < iSegmentNum; iCount++) { + pstVmMapping = pstVmMappingSegment + iCount; + + if (BBOX_TRUE == pstVmMapping->iIsRemoveFlags) { + pstVmMapping->uiWriteSize = 0; + (*piValidSegmentNum)--; + continue; + } + + /* If the segment is a code segment or a non-anonymous segment that cannot be read or written, + set the size written to the core file to 0, otherwise the size is calculated */ +#if defined(__ARM_ARCH_5TE__) || (defined(__ARM_ARCH_7A__)) || (defined(__aarch64__)) + if ((pstVmMapping->iFlags & (PF_ANONYMOUS | PF_W | PF_R)) == 0 || (pstVmMapping->iFlags & PF_MAPPEDFILE)) { +#else + if ((pstVmMapping->iFlags & (PF_ANONYMOUS | PF_W | PF_R)) == 0 || (pstVmMapping->iFlags & PF_X) || + (pstVmMapping->iFlags & PF_MAPPEDFILE)) { +#endif + pstVmMapping->uiWriteSize = + ((pstVmMapping->iFlags & PF_VDSO) ? (pstVmMapping->uiEndAddress - pstVmMapping->uiStartAddress) : 0); + + } else { + pstVmMapping->uiWriteSize = pstVmMapping->uiEndAddress - pstVmMapping->uiStartAddress; + + bbox_print(PRINT_DBG, "Segment[%d]: set writesize = %zu.\n", iCount, pstVmMapping->uiWriteSize); + } + + /* mark a segment not write into core file + if it cannot be read, discribes equipment segment or segment size is 0 or vvar segement */ + if (((pstVmMapping->iFlags & PF_R) == 0) || pstVmMapping->uiStartAddress == pstVmMapping->uiEndAddress || + (pstVmMapping->iFlags & PF_VVAR) || + (pstVmMapping->iFlags & PF_DEVICE)) { + pstVmMapping->uiWriteSize = 0; + (*piValidSegmentNum)--; + pstVmMapping->iIsRemoveFlags = BBOX_TRUE; + } + + if (pstVmMapping->iIsRemoveFlags != BBOX_TRUE) { + bbox_print(PRINT_DBG, + "segment[%d]: %zu %zu %u\n", + iCount, + pstVmMapping->uiOffset, + pstVmMapping->uiWriteSize, + pstVmMapping->iFlags); + } + } + + return RET_OK; +} + +/* + * fill the structure, which should be written into core file and discribes mapping segment of process address space. + * in : struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to mapping segment structure. + * int iSegmentNum - count of mapping segment in address space + * int *piValidSegmentNum - count of valid mapping segment in address space, + * which need to be written into core file. + * return RET_OK or RET_ERR. + */ +static int BBOX_FillVmMappingInfo(struct BBOX_VM_MAPS* pstVmMappingSegment, int* piSegmentNum, int* piValidSegmentNum) +{ + int iResult = RET_ERR; + + if (NULL == pstVmMappingSegment || NULL == piValidSegmentNum || NULL == piSegmentNum) { + bbox_print(PRINT_ERR, + "BBOX_FillVmMappingInfo parameters is invalid: pstVmMappingSegment, " \ + "piValidSegmentNum or piSegmentNum is NULL.\n"); + return RET_ERR; + } + + /* read /proc/self/maps and fill struct BBOX_VM_MAPS. */ + iResult = BBOX_GetMappingSegment(pstVmMappingSegment, piSegmentNum); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_GetMappingSegment is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + + /* judge if it need to be written into core file and the size to be written in + for every segment in process address space. */ + iResult = BBOX_VmMappingSizeDump(pstVmMappingSegment, *piSegmentNum, piValidSegmentNum); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_VmMappingSizeDump is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + + bbox_print(PRINT_LOG, "Fill Vm mapping segment info success.\n"); + return RET_OK; +} + +/* + * read /proc/self/auxv and get count of auxv and VDSO address + * in : union BBOX_VM_VDSO *pstVmVDSO - pointer to structure that decribes VDSO segment. + * return : iAuxvNum - count of auxv, it is not a negative. + * RET_ERR + */ +static int BBOX_GetVDSOAndVmAuxvNum(union BBOX_VM_VDSO* pstVmVDSO) +{ + int iAuxvFd = -1; + ssize_t iReadSize = RET_ERR; + int iAuxvNum = 0; + BBOX_AUXV_T stAuxv; + errno_t rc = EOK; + if (NULL == pstVmVDSO) { + bbox_print(PRINT_ERR, "BBOX_GetVDSOAndVmAuxvNum parameters is invalid: pstVmVDSO is NULL.\n"); + return RET_ERR; + } + + pstVmVDSO->pVDSOEhdr = NULL; + + /* read /proc/self/auxv, get count of Auxv written into core file and load address of VDSO. */ + BBOX_NOINTR(iAuxvFd = sys_open(THREAD_SELF_AUXV_FILE, O_RDONLY, 0)); + if (iAuxvFd < 0) { + bbox_print(PRINT_ERR, "sys_open is failed, iAuxvFd = %d.\n", iAuxvFd); + return RET_ERR; + } + + do { + iReadSize = RET_ERR; + rc = memset_s(&stAuxv, sizeof(BBOX_AUXV_T), 0, sizeof(BBOX_AUXV_T)); + securec_check_c(rc, "\0", "\0"); + + BBOX_NOINTR(iReadSize = sys_read(iAuxvFd, &stAuxv, sizeof(BBOX_AUXV_T))); + if (RET_ERR == iReadSize) { + bbox_print(PRINT_ERR, "sys_read is failed, iReadSize = %zd.\n", iReadSize); + BBOX_NOINTR(sys_close(iAuxvFd)); + return RET_ERR; + } + if (iReadSize != sizeof(BBOX_AUXV_T)) { + break; + } + + iAuxvNum++; + if (stAuxv.a_type == AT_SYSINFO_EHDR) { + /* get VDSO load address from AT_SYSINFO_EHDR of Auxv. */ + pstVmVDSO->pVDSOEhdr = (BBOX_EHDR*)stAuxv.a_un.a_val; + } + } while (stAuxv.a_type != AT_NULL); + + BBOX_NOINTR(sys_close(iAuxvFd)); + + return iAuxvNum; +} + +/* + * judge if the VDSO address we got is valid + * in : BBOX_EHDR *pstVDSOEhdr - pointer to elf header of VDSO. + * size_t uiStartAddress - start address of a segment in address space. + * size_t uiEndAddress - end address of a segment in address space. + * return : RET_OK - success + * RET_BBOX_VDSO_INVALID - failed + */ +static int BBOX_CheakVDSOEhdr(BBOX_EHDR* pstVDSOEhdr, size_t uiStartAddress, size_t uiEndAddress) +{ + int iCount = 0; + BBOX_PHDR* pstVDSOPhdr = NULL; + + if (NULL == pstVDSOEhdr) { + bbox_print(PRINT_ERR, "BBOX_CheakVDSOEhdr parameters is invalid: pstVDSOEhdr is NULL.\n"); + + return RET_ERR; + } + + const size_t uiEhdrAddress = (size_t)pstVDSOEhdr; + + if (uiEhdrAddress & (sizeof(size_t) - 1)) { + /* not aligned properly */ + bbox_print(PRINT_ERR, "VDSO is invalid: not aligned properly.\n"); + return RET_BBOX_VDSO_INVALID; + } + + if (uiEndAddress <= uiEhdrAddress + sizeof(BBOX_EHDR)) { + /* pstVDSOEhdr has Incomplete head */ + bbox_print(PRINT_ERR, "VDSO head is invalid: pstVDSOEhdr has Incomplete head.\n"); + return RET_BBOX_VDSO_INVALID; + } + + if (pstVDSOEhdr->e_phoff & (sizeof(size_t) - 1)) { + /* not aligned properly */ + bbox_print(PRINT_ERR, "VDSO Ehdr is invalid: not aligned properly.\n"); + return RET_BBOX_VDSO_INVALID; + } + + pstVDSOPhdr = (BBOX_PHDR*)(uiEhdrAddress + pstVDSOEhdr->e_phoff); + if ((size_t)pstVDSOPhdr <= uiStartAddress || uiEndAddress <= (size_t)(pstVDSOPhdr + pstVDSOEhdr->e_phnum)) { + /* VDSOPhdr is incompleted */ + bbox_print(PRINT_ERR, "VDSO Phdr is invalid: VDSOPhdr is incompleted.\n"); + return RET_BBOX_VDSO_INVALID; + } + + if (pstVDSOPhdr[0].p_type != PT_LOAD || pstVDSOPhdr[0].p_vaddr != uiStartAddress || + pstVDSOPhdr[0].p_vaddr + pstVDSOPhdr[0].p_memsz >= uiEndAddress) { + bbox_print(PRINT_ERR, "VDSO Phdr is invalid.\n"); + return RET_BBOX_VDSO_INVALID; + } + + for (iCount = 1; iCount < pstVDSOEhdr->e_phnum; iCount++) { + /* VDSO has multiple PT_LOAD segments. */ + if (pstVDSOPhdr[iCount].p_type == PT_LOAD) { + bbox_print(PRINT_ERR, "VDSO Phdr is invalid: VDSO has multiple PT_LOAD segments.\n"); + return RET_BBOX_VDSO_INVALID; + } + + /* VDSOPhdr is not aligned properly. */ + if (pstVDSOPhdr[0].p_vaddr & (sizeof(size_t) - 1)) { + bbox_print(PRINT_ERR, "VDSO Phdr is invalid: VDSOPhdr is not aligned properly.\n"); + return RET_BBOX_VDSO_INVALID; + } + + /* Phdr data range is out of bounds */ + if (pstVDSOPhdr[iCount].p_vaddr != uiStartAddress || + (pstVDSOPhdr[iCount].p_vaddr + pstVDSOPhdr[iCount].p_memsz >= uiEndAddress)) { + bbox_print(PRINT_ERR, "VDSO Phdr is invalid: Phdr data range is out of bounds.\n"); + return RET_BBOX_VDSO_INVALID; + } + } + + return RET_OK; +} + +/* + * judge if the VDSO address we got is valid and if it need to be written into core file. + * in : struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to mapping segment structure. + * int iSegmentNum - count of mapping segment in address space. + * union BBOX_VM_VDSO *pstVmVDSO - pointer to VDSO structure. + * return RET_OK or RET_ERR + */ +static int BBOX_CheckVDSO(struct BBOX_VM_MAPS* pstVmMappingSegment, int iSegmentNum, union BBOX_VM_VDSO* pstVmVDSO) +{ + int iCount = 0; + int iResult = RET_ERR; + struct BBOX_VM_MAPS* pstVmMappingTemp = NULL; + + if (NULL == pstVmMappingSegment || NULL == pstVmVDSO || iSegmentNum <= 0) { + bbox_print(PRINT_ERR, + "BBOX_CheckVDSO parameters is invalid: pstVmMappingSegment or "\ + "pstVmVDSO is NULL, iSegmentNum = %d.\n", + iSegmentNum); + + return RET_ERR; + } + + for (iCount = 0; iCount < iSegmentNum; iCount++) { + pstVmMappingTemp = (pstVmMappingSegment + iCount); + if (BBOX_FALSE == pstVmMappingTemp->iIsRemoveFlags) { + /* traverse segment, and judge if VDSO address is in segment scope. */ + if ((pstVmMappingTemp->iFlags & PF_R) && (pstVmMappingTemp->uiStartAddress <= pstVmVDSO->uiVDSOAddress) && + (pstVmMappingTemp->uiEndAddress > pstVmVDSO->uiVDSOAddress)) { + /* judge if VDSO address is valid. */ + iResult = BBOX_CheakVDSOEhdr( + pstVmVDSO->pVDSOEhdr, pstVmMappingTemp->uiStartAddress, pstVmMappingTemp->uiEndAddress); + if (RET_BBOX_VDSO_INVALID == iResult) { + pstVmVDSO->pVDSOEhdr = NULL; + } else if (RET_ERR == iResult) { + bbox_print(PRINT_ERR, "BBOX_CheakVDSOEhdr is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + break; + } + } + } + + if (iCount == iSegmentNum) { + pstVmVDSO->uiVDSOAddress = 0; + } + + return RET_OK; +} + +/* + * get count of VDSO writen into core file. + * in : union BBOX_VM_VDSO *pstVmVDSO pointer to VDSO union structure. + * return : iExtraPhdrNum - result + * RET_ERR - failed + */ +static int BBOX_GetExtraPhdrNum(union BBOX_VM_VDSO* pstVmVDSO) +{ + int iExtraPhdrNum = 0; + int iCount = 0; + BBOX_PHDR* pVDSOPhdr = NULL; + + if (NULL == pstVmVDSO) { + bbox_print(PRINT_ERR, "BBOX_GetExtraPhdrNum parameters is invalid: pstVmVDSO is NULL.\n"); + + return RET_ERR; + } else { + if (0 == pstVmVDSO->uiVDSOAddress) { + return iExtraPhdrNum; + } + + /* if segment in VDSO is not PT_LOAD, add it into core file. */ + pVDSOPhdr = (BBOX_PHDR*)(pstVmVDSO->uiVDSOAddress + pstVmVDSO->pVDSOEhdr->e_phoff); + for (iCount = 0; iCount < pstVmVDSO->pVDSOEhdr->e_phnum; iCount++) { + if (pVDSOPhdr[iCount].p_type != PT_LOAD) { + iExtraPhdrNum++; + } + } + } + + bbox_print(PRINT_DBG, "Extra Phdr Num: iExtraPhdrNum = %d.\n", iExtraPhdrNum); + + return iExtraPhdrNum; +} + +/* + * fill structure that discribe VDSO + * in : int *piAuxvNum - count of Auxv + * int *piExtraPhdrNum - count of VDSO written into core file + * int iSegmentNum - count of mapping segment in address space + * union BBOX_VM_VDSO *pstVmVDSO - pointer to structure that discribe VDSO segment. + * struct BBOX_VM_MAPS *pstVmMappingSegment - pointer to structure that discribes mapping segment. + * return RET_OK or RET_ERR. + */ +static int BBOX_FillVDSOInfo(int* piAuxvNum, int* piExtraPhdrNum, int iSegmentNum, union BBOX_VM_VDSO* pstVmVDSO, + struct BBOX_VM_MAPS* pstVmMappingSegment) +{ + int iResult = RET_ERR; + + if (NULL == piAuxvNum || NULL == piExtraPhdrNum || NULL == pstVmVDSO || NULL == pstVmMappingSegment || + iSegmentNum <= 0) { + bbox_print(PRINT_ERR, + "BBOX_FillVDSOInfo parameters is invalid: piAuxvNum, piExtraPhdrNum," \ + "pstVmVDSO or pstVmMappingSegment is NULL, iSegmentNum = %d.\n", + iSegmentNum); + + return RET_ERR; + } + + /* get VDSO, load address of VDSO segment and count of Auxv be written into core file. */ + iResult = BBOX_GetVDSOAndVmAuxvNum(pstVmVDSO); + if (RET_ERR == iResult) { + bbox_print(PRINT_ERR, "BBOX_GetVDSOAndVmAuxvNum is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + *piAuxvNum = iResult; + + /* check if VDSO is valid */ + iResult = BBOX_CheckVDSO(pstVmMappingSegment, iSegmentNum, pstVmVDSO); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_CheckVDSO is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + /* get the number of additional segments, which means that add VDSO into segment. */ + iResult = BBOX_GetExtraPhdrNum(pstVmVDSO); + if (RET_ERR == iResult) { + bbox_print(PRINT_ERR, "BBOX_GetExtraPhdrNum is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + (*piExtraPhdrNum) += iResult; + + bbox_print(PRINT_LOG, "Fill VDSO info success.\n"); + return RET_OK; +} + +/* + * get the process run time and write it into structure. + * in : char *pszReadFile - store the character array read from file. + * int iReadSize - count of character in array. + * struct BBOX_ELF_PRPSSTATUS *pstPrPsStatus - pointer to structure that store process status information. + * return RET_OK or RET_ERR. + */ +static int BBOX_SetPsStatusTime(char* pszReadFile, int iReadSize, struct BBOX_ELF_PRPSSTATUS* pstPrPsStatus) +{ + int iFlag = 1; + const int iUTimePos = 13; /* the 13th is User time */ + const int iSTimePos = 14; /* the 14th is System time */ + const int iCUTimePos = 15; /* the 15th is Cumulative user time */ + const int iCSTimePos = 16; /* the 16th is Cumulative system time */ + const int iPendingSigPos = 30; /* the 30th is Pending signals */ + const int iHeldSigPos = 31; /* the 31th is Held signals */ + unsigned int uCount = 0; + unsigned int uItemNum = 0; + char* pcSignalstr = 0; + char* pStatItem[BBOX_STAT_ITEM_NUM]; + + if (NULL == pszReadFile || NULL == pstPrPsStatus || iReadSize <= 0) { + bbox_print(PRINT_ERR, + "BBOX_SetPsStatusTime parameters is invalid: pszReadFile or pstPrPsStatus is NULL, iReadSize = %d.\n", + iReadSize); + + return RET_ERR; + } + + for (uCount = 0; uCount < (unsigned int)iReadSize; uCount++) { + /* use pointer to record the string be divided. */ + if (iFlag) { + pStatItem[uItemNum++] = (pszReadFile + uCount); + iFlag = 0; + } + + /* convert ' ' to '\0' */ + if (pszReadFile[uCount] == ' ') { + pszReadFile[uCount] = '\0'; + iFlag = 1; + } + } + + /* the 13th is User time */ + bbox_print(PRINT_DBG, "User Time : %s\n", pStatItem[iUTimePos]); + (void)BBOX_StringToTime(pStatItem[iUTimePos], &(pstPrPsStatus->stUserTime)); + + /* the 14th is System time */ + bbox_print(PRINT_DBG, "System Time : %s\n", pStatItem[iSTimePos]); + (void)BBOX_StringToTime(pStatItem[iSTimePos], &(pstPrPsStatus->stSystemTime)); + + /* the 15th is Cumulative user time */ + bbox_print(PRINT_DBG, "Cumulative user Time : %s\n", pStatItem[iCUTimePos]); + (void)BBOX_StringToTime(pStatItem[iCUTimePos], &(pstPrPsStatus->stCumulativeUserTime)); + + /* the 16th is Cumulative system time */ + bbox_print(PRINT_DBG, "Cumulative system : %s\n", pStatItem[iCSTimePos]); + (void)BBOX_StringToTime(pStatItem[iCSTimePos], &(pstPrPsStatus->stCumulativeSystemTime)); + + /* the 30th is Pending signals */ + pcSignalstr = pStatItem[iPendingSigPos]; + pstPrPsStatus->ulSigPend = 0; + while (*pcSignalstr != '\0') { + pstPrPsStatus->ulSigPend = 10 * pstPrPsStatus->ulSigPend + (*pcSignalstr - '0'); + pcSignalstr++; + } + + /* the 31th is Held signals */ + pcSignalstr = pStatItem[iHeldSigPos]; + pstPrPsStatus->ulSigHold = 0; + while (*pcSignalstr != '\0') { + pstPrPsStatus->ulSigHold = 10 * pstPrPsStatus->ulSigHold + (*pcSignalstr - '0'); + pcSignalstr++; + } + + return RET_OK; +} + +/* + * get the process run time + * in : struct BBOX_ELF_PRPSSTATUS *pstPrPsStatus - pointer to structure that store process status information. + * return RET_OK or RET_ERR. + */ +static int BBOX_FillPsStatusTimeInfo(struct BBOX_ELF_PRPSSTATUS* pstPrPsStatus) +{ + ssize_t iReadSize = RET_ERR; + int iStatFileFd = -1; + int iResult = RET_ERR; + char szReadFile[BBOX_BUFF_LITTLE_SIZE]; + errno_t rc = EOK; + if (NULL == pstPrPsStatus) { + bbox_print(PRINT_ERR, "BBOX_FillPsStatusTimeInfo parameters is invalid: pstPrPsStatus is NULL.\n"); + + return RET_ERR; + } + + rc = memset_s(szReadFile, sizeof(szReadFile), 0, sizeof(szReadFile)); + securec_check_c(rc, "\0", "\0"); + + /* read /proc/self/stat */ + BBOX_NOINTR(iStatFileFd = sys_open(THREAD_SELF_STAT_FILE, O_RDONLY, 0)); + if (iStatFileFd < 0) { + bbox_print(PRINT_ERR, "sys_open is failed iStatFileFd = %d.\n", iStatFileFd); + + return RET_ERR; + } + + do { + iReadSize = RET_ERR; + rc = memset_s(szReadFile, sizeof(szReadFile), 0, sizeof(szReadFile)); + securec_check_c(rc, "\0", "\0"); + + BBOX_NOINTR(iReadSize = sys_read(iStatFileFd, szReadFile, sizeof(szReadFile))); + if (iReadSize > 0) { + iResult = BBOX_SetPsStatusTime(szReadFile, iReadSize, pstPrPsStatus); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_SetPsStatusTime is failed, iResult = %d.\n", iResult); + BBOX_NOINTR(sys_close(iStatFileFd)); + return RET_ERR; + } + } else if (iReadSize < 0) { + bbox_print(PRINT_ERR, "failed to read status file, iResult = %zd.\n", iReadSize); + BBOX_NOINTR(sys_close(iStatFileFd)); + return RET_ERR; + } + } while (iReadSize > 0); + + BBOX_NOINTR(sys_close(iStatFileFd)); + + return RET_OK; +} + +/* + * get register information of parent process, get user data structure information. + * in : struct BBOX_ELF_PRPSINFO *pstPrPsInfo - pointer to structure that store process status information. + * pid_t tMainPid - main process pid. + * return RET_OK or RET_ERR. + */ +static int BBOX_FillPrPsInfo(struct BBOX_ELF_PRPSINFO* pstPrPsInfo, pid_t tMainPid) +{ + char szBuff[BBOX_BUFF_SIZE]; + + ssize_t iReadSize = 0; + ssize_t iLen = 0; + int iCommandLineFileFd = -1; + char* pExePathName = szBuff; + char* pTemp = NULL; + errno_t rc = EOK; + if (NULL == pstPrPsInfo) { + bbox_print(PRINT_ERR, "BBOX_FillPrPsInfo parameters is invalid: pstPrPsInfo is NULL.\n"); + + return RET_ERR; + } + + rc = memset_s(pstPrPsInfo, sizeof(struct BBOX_ELF_PRPSINFO), 0, sizeof(struct BBOX_ELF_PRPSINFO)); + securec_check_c(rc, "\0", "\0"); + + pstPrPsInfo->cSname = 'R'; + pstPrPsInfo->cNice = (signed char)sys_getpriority(PRIO_PROCESS, 0); +#if (defined(__x86_64__)) || (defined(__aarch64__)) + pstPrPsInfo->tUid = (uint32_t)sys_geteuid(); + pstPrPsInfo->tGid = (uint32_t)sys_getegid(); +#elif (defined(__i386__)) || (defined(__ARM_ARCH_5TE__)) || (defined(__ARM_ARCH_7A__)) + pstPrPsInfo->tUid = (uint16_t)sys_geteuid(); + pstPrPsInfo->tGid = (uint16_t)sys_getegid(); +#endif + pstPrPsInfo->tpid = tMainPid; + pstPrPsInfo->tPpid = sys_getppid(); + pstPrPsInfo->tPgrp = sys_getpgrp(); + pstPrPsInfo->tSid = sys_getsid(0); + + rc = memset_s(szBuff, sizeof(szBuff), 0, sizeof(szBuff)); + securec_check_c(rc, "\0", "\0"); + + iReadSize = sys_readlink(THREAD_SELF_EXE_FILE, szBuff, sizeof(szBuff)); + iLen = 0; + for (pTemp = szBuff; (*pTemp != '\000') && ((iReadSize--) > 0); pTemp++) { + /* get the command name of the program to run (/bin/bash --> bash) */ + if (*pTemp == '/') { + pExePathName = pTemp + 1; + iLen = 0; + } else { + iLen++; + } + } + rc = memcpy_s(pstPrPsInfo->cFname, + sizeof(pstPrPsInfo->cFname), + pExePathName, + (iLen > (ssize_t)sizeof(pstPrPsInfo->cFname) ? sizeof(pstPrPsInfo->cFname) : iLen)); + securec_check_c(rc, "\0", "\0"); + + /* read /proc/self/command, get parameter list of command. */ + BBOX_NO_INTR(iCommandLineFileFd = sys_open(THREAD_SELF_COMMAND_LINE_FILE, O_RDONLY, 0)); + if (iCommandLineFileFd < 0) { + bbox_print(PRINT_ERR, "sys_open is failed: iCommandLineFileFd = %d.\n", iCommandLineFileFd); + + return RET_ERR; + } + + BBOX_NO_INTR(iReadSize = sys_read(iCommandLineFileFd, pstPrPsInfo->cPsargs, sizeof(pstPrPsInfo->cPsargs))); + if (iReadSize < 0) { + BBOX_NO_INTR(sys_close(iCommandLineFileFd)); + return RET_ERR; + } + + for (pTemp = pstPrPsInfo->cPsargs; (iReadSize--) > 0; pTemp++) { + /* convert '\0' to ' ' so that all of it can be print out one time. */ + if (*pTemp == '\000') { + *pTemp = ' '; + } + } + + BBOX_NO_INTR(sys_close(iCommandLineFileFd)); + + bbox_print(PRINT_LOG, "Fill Prpsinfo Info success.\n"); + return RET_OK; +} + +/* + * get process register content. + * in : Frame *pFrame - pointer for writing core file. + * struct BBOX_ELF_NOTE_INFO *pstNoteInfo - pointer to structure of note segment distription. + * pid_t *ptPids - pointer to process id array. + * int iSegmentNum - count of mapping segment in address space. + * int *piPhdrSum - sum count of mapping segment and VDSO segment in core file. + * int iThreadNum - size of process array. + * return RET_OK or RET_ERR. + */ +static int BBOX_FillPrPsStatusRegs(Frame* pFrame, struct BBOX_ELF_NOTE_INFO* pstNoteInfo, pid_t* ptPids, int iThreadNum) +{ + char acBuff[BBOX_BUFF_LITTLE_SIZE]; + unsigned int uCount = 0; + errno_t rc = EOK; + + if (NULL == pFrame || NULL == pstNoteInfo || NULL == ptPids || iThreadNum <= 0) { + bbox_print(PRINT_ERR, + "BBOX_FillPrPsStatusRegs parameters is invalid: pFrame, pstNoteInfo or " \ + "ptPids is NULL, iThreadNum = %d.\n", + iThreadNum); + + return RET_ERR; + } + + struct BBOX_THREAD_NOTE_INFO* pstThreadNoteInfo = pstNoteInfo->pstThreadNoteInfo; + + for (uCount = 0; uCount < (unsigned int)iThreadNum; uCount++) { + rc = memset_s(acBuff, BBOX_BUFF_LITTLE_SIZE, 0xFF, sizeof(acBuff)); + securec_check_c(rc, "\0", "\0"); + +#if defined(__aarch64__) + /* get cpu register information of pid[i], run if err, try best to create core file. */ + void* pregset = (void*)NT_PRSTATUS; + struct iovec io_vec; + + io_vec.iov_base = acBuff; + io_vec.iov_len = sizeof(struct CPURegs); + if (RET_OK == sys_ptrace(PTRACE_GETREGSET, ptPids[uCount], pregset, &io_vec)) { + rc = memcpy_s(&((pstThreadNoteInfo + uCount)->stPrpsstatus.stRegisters), + sizeof(struct CPURegs), + acBuff, + sizeof(struct CPURegs)); + securec_check_c(rc, "\0", "\0"); + rc = memset_s(acBuff, sizeof(acBuff), 0xFF, sizeof(acBuff)); + securec_check_c(rc, "\0", "\0"); + } +#else + /* get cpu register information of pid[i], run if err, try best to create core file. */ + if (RET_OK == sys_ptrace(PTRACE_GETREGS, ptPids[uCount], acBuff, acBuff)) { + rc = memcpy_s(&((pstThreadNoteInfo + uCount)->stPrpsstatus.stRegisters), + sizeof(struct CPURegs), + acBuff, + sizeof(struct CPURegs)); + securec_check_c(rc, "\0", "\0"); + + if (ptPids[uCount] == pstNoteInfo->tMainPid) { + SET_FRAME(*(Frame*)pFrame, (pstThreadNoteInfo + uCount)->stPrpsstatus.stRegisters); + } + + rc = memset_s(acBuff, BBOX_BUFF_LITTLE_SIZE, 0xFF, sizeof(acBuff)); + securec_check_c(rc, "\0", "\0"); + } + + /* get fpu register information of pid[i], run if err, try best to create core file. */ + if (RET_OK == sys_ptrace(PTRACE_GETFPREGS, ptPids[uCount], acBuff, acBuff)) { + rc = memcpy_s(&((pstThreadNoteInfo + uCount)->stFpRegisters), + sizeof(struct BBOX_FPREGSET), + acBuff, + sizeof(struct BBOX_FPREGSET)); + securec_check_c(rc, "\0", "\0"); + rc = memset_s(acBuff, BBOX_BUFF_LITTLE_SIZE, 0xFF, sizeof(acBuff)); + securec_check_c(rc, "\0", "\0"); + } +#endif + +#if (defined(__i386__)) + + /* get sse register information of pid[i], run if err, try best to create core file. */ + if (RET_OK == sys_ptrace(PTRACE_GETFPXREGS, ptPids[uCount], acBuff, acBuff)) { + rc = memcpy_s(&((pstThreadNoteInfo + uCount)->stFpxRegisters), + sizeof(struct BBOX_FPXREGSET), + acBuff, + sizeof(struct BBOX_FPXREGSET)); + securec_check_c(rc, "\0", "\0"); + pstNoteInfo->iFpxRegistersFlag = BBOX_TRUE; + } else { + pstNoteInfo->iFpxRegistersFlag = BBOX_FALSE; + } +#else + + /* sse register information is stored in sse structure in x86-64. */ + pstNoteInfo->iFpxRegistersFlag = BBOX_FALSE; +#endif + + (pstThreadNoteInfo + uCount)->stPrpsstatus.tpid = ptPids[uCount]; + } + + return RET_OK; +} + +/* + * get user data structure information. + * in : struct BBOX_CORE_USER *pstCoreUser - pointer to strcuture that store user data. + * struct CPURegs *pstThreadRegs - struct pointer that store parent process register information. + * pid_t *ptPids - pointer to process id array. + * return RET_OK or RET_ERR. + */ +static int BBOX_GetParentRegs(struct BBOX_CORE_USER* pstCoreUser, struct CPURegs* pstThreadRegs, pid_t* ptPids) +{ + int iCount = 0; + + if (NULL == pstCoreUser || NULL == pstThreadRegs || NULL == ptPids) { + bbox_print(PRINT_ERR, + "BBOX_GetParentRegs parameters is invalid: pstCoreUser, " \ + "pstThreadRegs or ptPids is NULL.\n"); + + return RET_ERR; + } + + for (iCount = 0; iCount < (int)(sizeof(struct BBOX_CORE_USER) / sizeof(int)); iCount++) { + /* get register information of parent process and copy it into user data structure, + run if err, try best to create core file. */ + (void)sys_ptrace( + PTRACE_PEEKUSER, ptPids[0], (void*)(iCount * sizeof(int)), ((char*)pstCoreUser) + iCount * sizeof(int)); + } + + errno_t rc = memcpy_s(&(pstCoreUser->stRegisters), sizeof(struct CPURegs), pstThreadRegs, sizeof(struct CPURegs)); + securec_check_c(rc, "\0", "\0"); + + return RET_OK; +} + +/* + * fill note segment structure information of core file. + * in : Frame *pFrame - pointer of backstack information + * struct BBOX_ELF_NOTE_INFO *pstNoteInfo - pointer to execute note structure. + * pid_t *ptPids - pointer to process id. + * int iAuxvNum - count of Auxv that need to be written into core file. + * return RET_OK or RET_ERR. + */ +static int BBOX_FillNoteInfo(Frame* pFrame, struct BBOX_ELF_NOTE_INFO* pstNoteInfo, pid_t* ptPids, int iAuxvNum) +{ + unsigned int uCount = 0; + int iResult = -1; + struct BBOX_ELF_PRPSSTATUS stTempPrPsStatus; + errno_t rc = EOK; + if (NULL == pFrame || NULL == pstNoteInfo || NULL == ptPids || iAuxvNum < 0) { + bbox_print(PRINT_ERR, + "BBOX_FillNoteInfo parameters is invalid: pFrame, "\ + "pstNoteInfo or ptPids is NULL, iAuxvNum = %d.\n", + iAuxvNum); + + return RET_ERR; + } + + pstNoteInfo->iAuxvNoteInfoNum = iAuxvNum; + rc = memset_s(&(stTempPrPsStatus), sizeof(struct BBOX_ELF_PRPSSTATUS), 0, sizeof(struct BBOX_ELF_PRPSSTATUS)); + securec_check_c(rc, "\0", "\0"); + + rc = memset_s(&(pstNoteInfo->stCoreUser), sizeof(struct BBOX_CORE_USER), 0, sizeof(struct BBOX_CORE_USER)); + securec_check_c(rc, "\0", "\0"); + + rc = memset_s(&(pstNoteInfo->stPrpsinfo), sizeof(struct BBOX_ELF_PRPSINFO), 0, sizeof(struct BBOX_ELF_PRPSINFO)); + securec_check_c(rc, "\0", "\0"); + + struct BBOX_ELF_PRPSINFO* pstPrPsInfo = &(pstNoteInfo->stPrpsinfo); + struct BBOX_THREAD_NOTE_INFO* pstThreadPrpsstatus = pstNoteInfo->pstThreadNoteInfo; + int iThreadNum = pstNoteInfo->iThreadNoteInfoNum; + + /* get run status, priority, group id, parent process id of a process + and record them into struct BBOX_ELF_PRPSINFO. */ + iResult = BBOX_FillPrPsInfo(pstPrPsInfo, pstNoteInfo->tMainPid); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_FillPrPsInfo is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + /* get register, FPU and SSE information of process and record them into struct BBOX_THREAD_NOTE_INFO. */ + iResult = BBOX_FillPrPsStatusRegs(pFrame, pstNoteInfo, ptPids, iThreadNum); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_FillPrPsStatusRegs is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + /* get user data structure information and record it into struct BBOX_CORE_USER. */ + iResult = + BBOX_GetParentRegs(&(pstNoteInfo->stCoreUser), &(pstThreadPrpsstatus[0].stPrpsstatus.stRegisters), ptPids); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_GetParentRegs is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + /* get process time information and record it into struct BBOX_ELF_PRPSSTATUS. */ + iResult = BBOX_FillPsStatusTimeInfo(&stTempPrPsStatus); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_FillPsStatusTimeInfo is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + for (uCount = 0; uCount < (unsigned int)iThreadNum; uCount++) { + (pstThreadPrpsstatus + uCount)->stPrpsstatus.tPpid = pstNoteInfo->stPrpsinfo.tPpid; + (pstThreadPrpsstatus + uCount)->stPrpsstatus.tPgrp = pstNoteInfo->stPrpsinfo.tPgrp; + (pstThreadPrpsstatus + uCount)->stPrpsstatus.tSid = pstNoteInfo->stPrpsinfo.tSid; + (pstThreadPrpsstatus + uCount)->stPrpsstatus.tFpvalid = BBOX_TRUE; + + (pstThreadPrpsstatus + uCount)->stPrpsstatus.stUserTime.lTvSec = stTempPrPsStatus.stUserTime.lTvSec; + (pstThreadPrpsstatus + uCount)->stPrpsstatus.stUserTime.lTvMicroSec = stTempPrPsStatus.stUserTime.lTvMicroSec; + + (pstThreadPrpsstatus + uCount)->stPrpsstatus.stSystemTime.lTvSec = stTempPrPsStatus.stSystemTime.lTvSec; + (pstThreadPrpsstatus + uCount)->stPrpsstatus.stSystemTime.lTvMicroSec = + stTempPrPsStatus.stSystemTime.lTvMicroSec; + + (pstThreadPrpsstatus + uCount)->stPrpsstatus.stCumulativeUserTime.lTvSec = + stTempPrPsStatus.stCumulativeUserTime.lTvSec; + (pstThreadPrpsstatus + uCount)->stPrpsstatus.stCumulativeUserTime.lTvMicroSec = + stTempPrPsStatus.stCumulativeUserTime.lTvMicroSec; + + (pstThreadPrpsstatus + uCount)->stPrpsstatus.stCumulativeSystemTime.lTvSec = + stTempPrPsStatus.stCumulativeSystemTime.lTvSec; + (pstThreadPrpsstatus + uCount)->stPrpsstatus.stCumulativeSystemTime.lTvMicroSec = + stTempPrPsStatus.stCumulativeSystemTime.lTvMicroSec; + } + + return RET_OK; +} + +/* + * create symbol table + * in : char *pBuffer - buffer + * unsigned int uiBufLen - buffer size + * return RET_OK or RET_ERR. + */ +int BBOX_GenerateStrTab(char* pBuffer, unsigned int uiBufLen) +{ + int iResult = 0; + unsigned int uCount = 0; + unsigned int uiAllStringSz = 0; + + /* string symbol table */ + char* pacShName[BBOX_SECTION_NUM] = {BBOX_ADDON_INFO, BBOX_LOG, BBOX_STR_TAB}; + + /* concatenate the above strings into the buffer, take care '\0'. */ + for (uCount = 0; uCount < BBOX_SECTION_NUM; uCount++) { + iResult = bbox_snprintf(pBuffer, uiBufLen, pacShName[uCount]); + if (iResult <= 0 || iResult > (int)uiBufLen) { + bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + + return RET_ERR; + } + + pBuffer += iResult; + uiBufLen -= iResult; + uiAllStringSz += iResult; + } + + return uiAllStringSz; +} + +/* + * get content that be written into section. + * return RET_OK or RET_ERR. + */ +void BBOX_FillSectionInfo(void) +{ + int iResult = RET_ERR; + errno_t rc = EOK; + + /* create symbol table, if occur err, don't return ,run and try best to create core file. */ + iResult = BBOX_GenerateStrTab(g_acBboxStrTabInfo, sizeof(g_acBboxStrTabInfo)); + if (iResult < 0) { + bbox_print(PRINT_ERR, "_BBOX_GenerateStrTab is failed.\n"); + } + + bbox_print(PRINT_LOG, "Generate section string table successful.\n"); + + /* Get system information, continue if failed, try best to create core file. */ + iResult = _BBOX_GetAddonInfo(g_acBboxAddonInfo, sizeof(g_acBboxAddonInfo)); + if (iResult < 0) { + bbox_print(PRINT_ERR, "_BBOX_GetAddonInfo is failed.\n"); + } + + bbox_print(PRINT_LOG, "Generate addition system info successful.\n"); + + g_stElfSectionInfo.uiSectionNum = BBOX_SECTION_NUM; + g_stElfSectionInfo.pstSection = g_stSectionInfo; + + /* BBOX_ADDONINFO */ + g_stSectionInfo[0].uiSectionType = SHT_NOTE; + g_stSectionInfo[0].pacSectionDesc = g_acBboxAddonInfo; + g_stSectionInfo[0].uiSectionDescSize = sizeof(g_acBboxAddonInfo); + g_stSectionInfo[0].uiSectionNameSize = sizeof(BBOX_ADDON_INFO); + rc = strncpy_s( + g_stSectionInfo[0].acSectionName, BBOX_SECTION_NAME_LEN, BBOX_ADDON_INFO, g_stSectionInfo[0].uiSectionNameSize); + securec_check_c(rc, "\0", "\0"); + + /* BBOX_LOG */ + g_stSectionInfo[1].uiSectionType = SHT_NOTE; + g_stSectionInfo[1].pacSectionDesc = g_acBBoxLog; + g_stSectionInfo[1].uiSectionDescSize = sizeof(g_acBBoxLog); + g_stSectionInfo[1].uiSectionNameSize = sizeof(BBOX_LOG); + rc = strncpy_s( + g_stSectionInfo[1].acSectionName, BBOX_SECTION_NAME_LEN, BBOX_LOG, g_stSectionInfo[1].uiSectionNameSize); + securec_check_c(rc, "\0", "\0"); + + /* .shstrtab */ + g_stSectionInfo[2].uiSectionType = SHT_STRTAB; + g_stSectionInfo[2].pacSectionDesc = g_acBboxStrTabInfo; + g_stSectionInfo[2].uiSectionDescSize = sizeof(g_acBboxStrTabInfo); + g_stSectionInfo[2].uiSectionNameSize = sizeof(BBOX_STR_TAB); + rc = strncpy_s( + g_stSectionInfo[2].acSectionName, BBOX_SECTION_NAME_LEN, BBOX_STR_TAB, g_stSectionInfo[2].uiSectionNameSize); + securec_check_c(rc, "\0", "\0"); + + bbox_print(PRINT_LOG, "Fill all section successful.\n"); + + return; +} + +/* + * fill every structure written into core file, include mapping, note, VDSO. + * in : Frame *pFrame - Write the structure pointer to the core file + * struct BBOX_VM_MAPS *pstVmMappingSegment - Pointer to the structure that describes the mapping segment + * int iSegmentNum - The number of mapping segments in the address space + * int *piPhdrSum - Sum of mapping and VDSO in core file + * pid_t *ptPids - A pointer to an array of process Numbers + * struct BBOX_ELF_NOTE_INFO *pstNoteInfo - Pointer to the structure that describes the note segment + * union BBOX_VM_VDSO *pstVmVDSO - Pointer to the structure that describes the VDSO segment + */ +static int BBOX_FillAllInfoOfCoreFile(Frame* pFrame, struct BBOX_VM_MAPS* pstVmMappingSegment, int* piSegmentNum, + int* piPhdrSum, pid_t* ptPids, struct BBOX_ELF_NOTE_INFO* pstNoteInfo, union BBOX_VM_VDSO* pstVmVDSO) +{ + int iResult = 1; + int iValidSegmentNum = 0; + int iAuxvNum = 0; + int iExtraPhdrNum = 0; + + if (NULL == pFrame || NULL == pstVmMappingSegment || NULL == piPhdrSum || NULL == ptPids || NULL == pstNoteInfo || + NULL == pstVmVDSO || NULL == piSegmentNum) { + bbox_print(PRINT_ERR, + "BBOX_FillAllInfoOfCoreFile parameters is invalid: pFrame, pstVmMappingSegment, " \ + "piPhdrSum, ptPids, pstNoteInfo, pstVmVDSO or piSegmentNum is NULL.\n"); + return RET_ERR; + } + + /* read /proc/self/maps, fill structure that discribes process address space information. */ + iResult = BBOX_FillVmMappingInfo(pstVmMappingSegment, piSegmentNum, &iValidSegmentNum); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_FillVmMappingInfo is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + + /* get number of auxv written into core file in /proc/self/auxv, record VDSO address. + Determines whether VDSO is written to the core file and the number of segments written, + record it into iSegmentNum */ + iResult = BBOX_FillVDSOInfo(&iAuxvNum, &iExtraPhdrNum, *piSegmentNum, pstVmVDSO, pstVmMappingSegment); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_FillVDSOInfo is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + + /* calculate all segment that need to be written into core file except note segment */ + *piPhdrSum = iValidSegmentNum + iExtraPhdrNum; + bbox_print(PRINT_DBG, + "Get all Phdr numbers success, iValidSegmentNum = %d, iExtraPhdrNum = %d, *piPhdrSum=%d.\n", + iValidSegmentNum, + iExtraPhdrNum, + *piPhdrSum); + + /* Fill struct BBOX_ELF_NOTE_INFO */ + iResult = BBOX_FillNoteInfo(pFrame, pstNoteInfo, ptPids, iAuxvNum); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_FillNoteInfo is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + + /* fill section */ + BBOX_FillSectionInfo(); + + bbox_print(PRINT_LOG, "Fill Note Info success.\n"); + + return RET_OK; +} + +/* + * create and open core file, get fd. + * in : Frame *pFrame - Pointer to the structure of the core file + * char *pFileName - Pointer to an array of core file names + * struct BBOX_WRITE_FDS *pstFileWriteFd - Pointer to a structure that describes the properties of a core file + */ +static int BBOX_OpenCoreFile(Frame* pFrame, const char* pFileName, struct BBOX_WRITE_FDS* pstFileWriteFd) +{ + char szCmd[BBOX_CMD_LEN]; + errno_t rc = EOK; + + if (NULL == pFrame || NULL == pstFileWriteFd) { + bbox_print(PRINT_ERR, + "BBOX_OpenCoreFile parameters is invalid: pFrame or "\ + "pstFileWriteFd is NULL.\n"); + return RET_ERR; + } + + rc = memset_s(szCmd, BBOX_CMD_LEN, 0, sizeof(szCmd)); + securec_check_c(rc, "\0", "\0"); + + /* If the user does not define the file name, set it to core.tid.lz4 */ + if (NULL != pFileName) { + bbox_snprintf(szCmd, BBOX_CMD_LEN, COMPRESSION_CMD, 1, pFileName); + } else { + bbox_snprintf(szCmd, BBOX_CMD_LEN, COMPRESSION_CMD_WITH_FILENAME, 1, ((Frame*)pFrame)->tid); + } + + /* create core file using the way of compression while writing */ + pstFileWriteFd->iWriteFd = sys_popen(szCmd, "w"); + if (pstFileWriteFd->iWriteFd < 0) { + bbox_print(PRINT_ERR, "sys_popen is failed, pstFileWriteFd->iWriteFd = %d.\n", pstFileWriteFd->iWriteFd); + return RET_ERR; + } + + pstFileWriteFd->uiMaxLength = ~(size_t)0; + + bbox_print(PRINT_LOG, "Open core file success.\n"); + + return RET_OK; +} + +/* + * write to file + * in : struct BBOX_WRITE_FDS *pstWriteFds - A pointer to core file. + * void *pWriteData - Points to what will be written to the file + * size_t uiWriteSize - The size of what will be written to the file + */ +static ssize_t BBOX_DoWrite(struct BBOX_WRITE_FDS* pstWriteFds, void* pWriteData, size_t uiWriteSize) +{ + ssize_t iRet = 0; + ssize_t iWriteCount = 0; + const ssize_t iMaxSize = (1024 * 1024 * 1024); // 1G + size_t uiSize = 0; + + if (NULL == pstWriteFds || NULL == pWriteData) { + bbox_print(PRINT_ERR, + "BBOX_DoWrite parameters is invalid: pstWriteFds or " + "pWriteData is NULL.\n"); + return RET_ERR; + } + + while (uiWriteSize) { + uiSize = uiWriteSize; + if (uiSize > (size_t)iMaxSize) { + uiSize = iMaxSize; + } + + BBOX_NOINTR(iRet = sys_write(pstWriteFds->iWriteFd, pWriteData, uiSize)); + if (iRet <= 0) { + bbox_print(PRINT_ERR, + "sys_write failed, iRet = %zd, " + "uiSize = %zu, errno = %d.\n", + iRet, + uiSize, + errno); + + return iRet; + } + + iWriteCount += iRet; + uiWriteSize -= iRet; + pWriteData = (char*)pWriteData + iRet; + } + + return iWriteCount; +} + +/* + * The file header Ehdr structure that populates the elf file + * in : BBOX_EHDR *pEhdr - The header structure of the core file + * int iPhdrSum - The number of segments written to the core file in the address space + */ +static int BBOX_FillEhdr(BBOX_EHDR* pstEhdr, int iPhdrSum) +{ + if (NULL == pstEhdr || iPhdrSum <= 0) { + bbox_print(PRINT_ERR, "BBOX_FillEhdr parameters is invalid: pstEhdr maybe NULL, iPhdrSum = %d.\n", iPhdrSum); + return RET_ERR; + } + + pstEhdr->e_ident[0] = ELFMAG0; + pstEhdr->e_ident[1] = (unsigned char)ELFMAG1; + pstEhdr->e_ident[2] = ELFMAG2; + pstEhdr->e_ident[3] = ELFMAG3; + pstEhdr->e_ident[4] = BBOX_ELF_CLASS; + pstEhdr->e_ident[5] = (unsigned char)BBOX_DetermineMsb(); /* Determine whether the system is large or small */ + pstEhdr->e_ident[6] = EV_CURRENT; + pstEhdr->e_type = ET_CORE; + pstEhdr->e_machine = ELF_ARCH; + pstEhdr->e_version = EV_CURRENT; + pstEhdr->e_phoff = sizeof(BBOX_EHDR); + pstEhdr->e_ehsize = sizeof(BBOX_EHDR); + pstEhdr->e_phentsize = sizeof(BBOX_PHDR); + +#if (defined(__i386__)) + pstEhdr->e_phnum = (Elf32_Half)(iPhdrSum + 1); /* Number of memory address space segments plus note segments */ + pstEhdr->e_shnum = (Elf32_Half)(BBOX_SECTION_NUM); +#elif (defined(__x86_64__)) || (defined(__ARM_ARCH_5TE__)) || (defined(__ARM_ARCH_7A__)) || (defined(__aarch64__)) + pstEhdr->e_phnum = (Elf64_Half)(iPhdrSum + 1); /* Number of memory address space segments plus note segments */ + pstEhdr->e_shnum = (Elf64_Half)(BBOX_SECTION_NUM); +#endif + + pstEhdr->e_shoff = sizeof(BBOX_EHDR) + (iPhdrSum + 1) * sizeof(BBOX_PHDR); + pstEhdr->e_shentsize = sizeof(BBOX_SHDR); + + pstEhdr->e_shstrndx = BBOX_SHSTR_INDEX; + + bbox_print(PRINT_LOG, "Fill Elf Ehdr success.\n"); + + return RET_OK; +} + +/* + * write file header of core to core file. + * in : int iPhdrSum - The number of segment in the core file + * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to core file + */ +static int BBOX_WriteElfEhdr(int iPhdrSum, struct BBOX_WRITE_FDS* pstFileWriteFd) +{ + int iResult = RET_ERR; + ssize_t iWriteSize = -1; + BBOX_EHDR stElfCoreHead; + errno_t rc = EOK; + if (NULL == pstFileWriteFd || iPhdrSum <= 0) { + bbox_print(PRINT_ERR, + "BBOX_WriteElfEhdr parameters is invalid: pstFileWriteFd may be NULL, " \ + "iPhdrSum = %d.\n", + iPhdrSum); + + return RET_ERR; + } + + rc = memset_s(&stElfCoreHead, sizeof(BBOX_EHDR), 0, sizeof(BBOX_EHDR)); + securec_check_c(rc, "\0", "\0"); + + iResult = BBOX_FillEhdr(&stElfCoreHead, iPhdrSum); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_FillEhdr is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + iWriteSize = BBOX_DoWrite(pstFileWriteFd, &stElfCoreHead, sizeof(BBOX_EHDR)); + if (iWriteSize == RET_ERR || sizeof(BBOX_EHDR) != iWriteSize) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iWriteSize = %zd\n", iWriteSize); + + return RET_ERR; + } + + bbox_print(PRINT_LOG, "Write Ehdr info to the core file success.\n"); + + return RET_OK; +} + +/* + * Fill the header of the note segment of the core file describes the structure + * in : struct BBOX_ELF_NOTE_INFO *pstNoteInfo - The content structure of the note segment + * int iPhdrSum - The number of segments written to the core file in the address space + * size_t *puiOffset - The starting offset of the next segment + * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to core file + */ +static int BBOX_WriteNotePhdr( + struct BBOX_ELF_NOTE_INFO* pstNoteInfo, int iPhdrSum, size_t* puiOffset, struct BBOX_WRITE_FDS* pstFileWriteFd) +{ + size_t uiOffSize = 0; + size_t uiFileSize = 0; + size_t uiCoreUserSize = 0; + size_t uiNoteSize = 0; + size_t uiAuxvSize = 0; + size_t uiNoteAlign = 0; + ssize_t iWriteSize = -1; + int iPageSize = sys_sysconf(_SC_PAGESIZE); /* Gets the system page size */ + BBOX_PHDR stElfPhdr; + + if (NULL == pstNoteInfo || NULL == puiOffset || NULL == pstFileWriteFd || iPhdrSum <= 0) { + bbox_print(PRINT_ERR, + "BBOX_WriteNotePhdr parameters is invalid: pstNoteInfo, puiOffset or pstFileWriteFd is NULL, " \ + "iPhdrSum = %d.\n", + iPhdrSum); + + return RET_ERR; + } + + errno_t rc = memset_s(&stElfPhdr, sizeof(BBOX_PHDR), 0, sizeof(BBOX_PHDR)); + securec_check_c(rc, "\0", "\0"); + + /* Calculates the starting position and size of the note segment in the core file */ + uiOffSize = sizeof(BBOX_EHDR) + (iPhdrSum + 1) * sizeof(BBOX_PHDR) + 3 * sizeof(BBOX_SHDR); + + /* Calculate the size of the user data */ + uiCoreUserSize = sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_CORE_USER); + + /* Calculate the size of the BBOX_ELF_PRPSSTATUS */ +#if defined(__aarch64__) + uiNoteSize = (pstNoteInfo->iThreadNoteInfoNum) * + (sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_ELF_PRPSSTATUS)); +#else + uiNoteSize = (pstNoteInfo->iThreadNoteInfoNum) * + (sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_ELF_PRPSSTATUS) + sizeof(BBOX_NHDR) + + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_FPREGSET)); +#endif + + if (pstNoteInfo->iFpxRegistersFlag) { + /* If the SSE register exists, add its size */ + uiNoteSize += (pstNoteInfo->iThreadNoteInfoNum) * + (sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_FPXREGSET)); + } + + /* Calculates the size of the Auxv written */ + uiAuxvSize = sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + (pstNoteInfo->iAuxvNoteInfoNum) * sizeof(BBOX_AUXV_T); + uiFileSize = sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_ELF_PRPSINFO) + uiCoreUserSize + + uiNoteSize + uiAuxvSize; + + stElfPhdr.p_type = PT_NOTE; + stElfPhdr.p_offset = uiOffSize; + stElfPhdr.p_filesz = uiFileSize; + *puiOffset = uiOffSize + uiFileSize; + + iWriteSize = BBOX_DoWrite(pstFileWriteFd, &stElfPhdr, sizeof(BBOX_PHDR)); + if (iWriteSize == RET_ERR || sizeof(BBOX_PHDR) != iWriteSize) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iWriteSize = %zd.\n", iWriteSize); + return RET_ERR; + } + + /* Calculate the size of the page alignment to fill, and calculate the starting position of the next segment */ + stElfPhdr.p_align = iPageSize; + uiNoteAlign = stElfPhdr.p_align - ((*puiOffset) % stElfPhdr.p_align); + if (uiNoteAlign == stElfPhdr.p_align) { + uiNoteAlign = 0; + } + + pstNoteInfo->uiNoteAlign = uiNoteAlign; + (*puiOffset) += uiNoteAlign; + + return RET_OK; +} + +/* + * Fill the header of the note segment of the core file describes the structure + * in : struct BBOX_VM_MAPS *pstVmMappingSegment - The content structure of the note segment + * int iSegmentNum - The number of segments written to the core file in the address space + * size_t *puiOffset - The starting offset of the next segment + * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to core file + */ +static int BBOX_WriteVmSegmentPhdr( + struct BBOX_VM_MAPS* pstVmMappingSegment, int iSegmentNum, size_t* puiOffset, struct BBOX_WRITE_FDS* pstFileWriteFd) +{ + size_t uiFileSize = 0; + ssize_t iWriteSize = -1; + unsigned int uCount = 0; + int iPageSize = sys_sysconf(_SC_PAGESIZE); + BBOX_PHDR stElfPhdr; + + if (NULL == pstVmMappingSegment || NULL == puiOffset || NULL == pstFileWriteFd || iSegmentNum <= 0) { + bbox_print(PRINT_ERR, + "BBOX_WriteVmSegmentPhdr parameters is invalid: pstVmMappingSegment, puiOffset or " + "pstFileWriteFd is NULL, iSegmentNum = %d.\n", + iSegmentNum); + return RET_ERR; + } + + errno_t rc = memset_s(&stElfPhdr, sizeof(BBOX_PHDR), 0, sizeof(BBOX_PHDR)); + securec_check_c(rc, "\0", "\0"); + + stElfPhdr.p_type = PT_LOAD; + stElfPhdr.p_align = iPageSize; + stElfPhdr.p_paddr = 0; + + for (uCount = 0; uCount < (unsigned int)iSegmentNum; uCount++) { + if (pstVmMappingSegment[uCount].iIsRemoveFlags == 0) { + /* calculate size */ + uiFileSize = (pstVmMappingSegment[uCount].uiEndAddress) - (pstVmMappingSegment[uCount].uiStartAddress); + stElfPhdr.p_offset = *puiOffset; /* offset */ + stElfPhdr.p_vaddr = (pstVmMappingSegment[uCount].uiStartAddress); /* start address in address space of segment */ + stElfPhdr.p_memsz = uiFileSize; /* size of segment in address space */ + + uiFileSize = (pstVmMappingSegment[uCount].uiWriteSize); /* size of segment in file */ + stElfPhdr.p_filesz = uiFileSize; + stElfPhdr.p_flags = (pstVmMappingSegment[uCount].iFlags) & PF_MASK; + + (*puiOffset) += uiFileSize; /* next offset of segment. */ + + iWriteSize = BBOX_DoWrite(pstFileWriteFd, &stElfPhdr, sizeof(BBOX_PHDR)); + if (iWriteSize == RET_ERR || sizeof(BBOX_PHDR) != iWriteSize) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iWriteSize = %zd.\n", iWriteSize); + + return RET_ERR; + } + } + } + + return RET_OK; +} + +/* + * Writes a part of the VDSO segment to the core file + * in : struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to the structure of the core file + * union BBOX_VM_VDSO *pstVmVDSO - The structure that points to the VDSO segment + * size_t *puiOffset - The starting offset pointer of the next segment + * return RET_OK or RET_ERR. + */ +static int BBOX_WriteVDSOPhdr(struct BBOX_WRITE_FDS* pstFileWriteFd, union BBOX_VM_VDSO* pstVmVDSO, size_t* puiOffset) +{ + size_t uiFileSize = 0; + ssize_t iWriteSize = -1; + unsigned int uCount = 0; + BBOX_PHDR stElfPhdr; + + if (NULL == pstFileWriteFd || NULL == pstVmVDSO || NULL == puiOffset) { + bbox_print(PRINT_ERR, + "BBOX_WriteVDSOPhdr parameters is invalid: pstFileWriteFd, pstVmVDSO or " \ + "puiOffset is NULL.\n"); + return RET_ERR; + } + + errno_t rc = memset_s(&stElfPhdr, sizeof(BBOX_PHDR), 0, sizeof(BBOX_PHDR)); + securec_check_c(rc, "\0", "\0"); + + if (pstVmVDSO->uiVDSOAddress) { + BBOX_PHDR* pstVDSOPhdr = (BBOX_PHDR*)(pstVmVDSO->uiVDSOAddress + pstVmVDSO->pVDSOEhdr->e_phoff); + + for (uCount = 0; uCount < pstVmVDSO->pVDSOEhdr->e_phnum; uCount++) { + if (pstVDSOPhdr[uCount].p_type != PT_LOAD) { + /* Write the non-load segment of VDSO to the core file */ + rc = memcpy_s(&stElfPhdr, sizeof(BBOX_PHDR), pstVDSOPhdr + uCount, sizeof(BBOX_PHDR)); + securec_check_c(rc, "\0", "\0"); + + uiFileSize = stElfPhdr.p_filesz; + stElfPhdr.p_offset = *puiOffset; + stElfPhdr.p_paddr = 0; + + iWriteSize = BBOX_DoWrite(pstFileWriteFd, &stElfPhdr, sizeof(BBOX_PHDR)); + if (iWriteSize == RET_ERR || sizeof(BBOX_PHDR) != iWriteSize) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iWriteSize = %zd.\n", iWriteSize); + + return RET_ERR; + } + + (*puiOffset) += uiFileSize; + } + } + } + + return RET_OK; +} + +/* + * Writes the header of section to the core file + * in : int iPhdrSum - The number of program headers in the core file + * struct BBOX_ELF_SECTION *pstSectionInfo - The starting offset pointer of the next segment + * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to the structure of the core file + * return RET_OK or RET_ERR. + */ +static int BBOX_WriteElfShdr( + struct BBOX_ELF_SECTION* pstSectionInfo, size_t* puiOffset, struct BBOX_WRITE_FDS* pstFileWriteFd) +{ + size_t uiOffSize = 0; + size_t uiFileSize = 0; + unsigned int uCount = 0; + int iSecNameOffset = 0; + BBOX_SHDR stElfShdr; + BBOX_SECTION_STRU* pstSection = NULL; + + for (uCount = 0; uCount < pstSectionInfo->uiSectionNum; uCount++) { + pstSection = (pstSectionInfo->pstSection) + uCount; + errno_t rc = memset_s(&stElfShdr, sizeof(BBOX_SHDR), 0, sizeof(BBOX_SHDR)); + securec_check_c(rc, "\0", "\0"); + + uiOffSize = *puiOffset; + uiFileSize = pstSection->uiSectionDescSize; + + /* name of section, it is not a string and it's value is the offset of string in string table. */ + stElfShdr.sh_name = iSecNameOffset; + stElfShdr.sh_type = pstSection->uiSectionType; /* type of section */ + stElfShdr.sh_offset = uiOffSize; + stElfShdr.sh_size = uiFileSize; + + BBOX_WRITE(pstFileWriteFd, &stElfShdr, sizeof(BBOX_SHDR)); + + bbox_print(PRINT_LOG, "Write section[%s] head to fill successed.\n", pstSection->acSectionName); + + *puiOffset = uiOffSize + uiFileSize; + iSecNameOffset += pstSection->uiSectionNameSize; + } + + bbox_print(PRINT_LOG, "Write all section head to fill successed.\n"); + + return RET_OK; +} + +/* + * Writes all the progrem header of core file to the core file + * in : int iPhdrSum - The number of program headers in the core file + * struct BBOX_ELF_NOTE_INFO *pstNoteInfo - A pointer to the structure of note segment + * int iSegmentNum - The number of mapping segments in the address space + * union BBOX_VM_VDSO *pstVmVDSO - A pointer to the structure of VDSO segment + * struct BBOX_VM_MAPS *pstVmMappingSegment - A pointer to the structure of mapping segment + * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to the structure of the core file + * return RET_OK or RET_ERR. + */ +static int BBOX_WriteElfHdr(int iPhdrSum, struct BBOX_ELF_NOTE_INFO* pstNoteInfo, int iSegmentNum, + union BBOX_VM_VDSO* pstVmVDSO, struct BBOX_VM_MAPS* pstVmMappingSegment, struct BBOX_WRITE_FDS* pstFileWriteFd) +{ + int iResult = RET_ERR; + size_t uiOffset = 0; + + if (NULL == pstNoteInfo || NULL == pstVmMappingSegment || NULL == pstVmVDSO || NULL == pstFileWriteFd || + iSegmentNum <= 0 || iPhdrSum <= 0) { + bbox_print(PRINT_ERR, + "BBOX_WriteElfPhdr parameters is invalid: pstNoteInfo, pstVmMappingSegment," \ + "pstVmVDSO or pstFileWriteFd in NULL, iSegmentNum = %d, iPhdrSum = %d.\n", + iSegmentNum, + iPhdrSum); + return RET_ERR; + } + + /* write Phdr of Note to core file. */ + iResult = BBOX_WriteNotePhdr(pstNoteInfo, iPhdrSum, &uiOffset, pstFileWriteFd); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteNotePhdr is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + bbox_print(PRINT_LOG, "Write Note Phdr info to the core file success.\n"); + + /* write Phdr of process address space to core file. */ + iResult = BBOX_WriteVmSegmentPhdr(pstVmMappingSegment, iSegmentNum, &uiOffset, pstFileWriteFd); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteVmSegmentPhdr is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + bbox_print(PRINT_LOG, "Write Vm mapping segment Phdr info to the core file success.\n"); + + /* write Phdr of VDSO to core file. */ + iResult = BBOX_WriteVDSOPhdr(pstFileWriteFd, pstVmVDSO, &uiOffset); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteVDSOPhdr is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + bbox_print(PRINT_LOG, "Write VDSO Phdr info to the core file success.\n"); + + /* write Shdr of section to core file. */ + iResult = BBOX_WriteElfShdr(&g_stElfSectionInfo, &uiOffset, pstFileWriteFd); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteElfShdr is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + bbox_print(PRINT_LOG, "Write section head info to the core file success.\n"); + + return RET_OK; +} + +/* + * Writes the prpsinfo to the core file + * in : struct BBOX_ELF_PRPSINFO *pstPrPsInfo - A pointer to prpsinfo + * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file + * return RET_OK or RET_ERR. + */ +static int BBOX_WritePrPsinfoToFile(struct BBOX_ELF_PRPSINFO* pstPrPsInfo, struct BBOX_WRITE_FDS* pstFileFds) +{ + BBOX_NHDR stElfNhdr; + + if (NULL == pstPrPsInfo || NULL == pstFileFds) { + bbox_print(PRINT_ERR, + "BBOX_WritePrPsinfoToFile parameters is invalid: pstPrPsInfo or pstFileFds is NULL.\n"); + + return RET_ERR; + } + + errno_t rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); + securec_check_c(rc, "\0", "\0"); + + stElfNhdr.n_namesz = BBOX_CORE_NAME_LENGTH; + stElfNhdr.n_descsz = sizeof(struct BBOX_ELF_PRPSINFO); + stElfNhdr.n_type = NT_PRPSINFO; + + if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || + BBOX_DoWrite(pstFileFds, (void*)BBOX_CORE_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH || + BBOX_DoWrite(pstFileFds, pstPrPsInfo, sizeof(struct BBOX_ELF_PRPSINFO)) != sizeof(struct BBOX_ELF_PRPSINFO)) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); + + return RET_ERR; + } + + bbox_print(PRINT_LOG, + "Write Prpsinfo size = %zd to the core file success.\n", + sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_ELF_PRPSINFO)); + + return RET_OK; +} + +/* + * Writes the user data information to the core file + * in : struct BBOX_ELF_PRPSINFO *pPrPsInfo - A pointer to User Core + * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file + * return RET_OK or RET_ERR. + */ +static int BBOX_WriteUserRegistersToFile(struct BBOX_CORE_USER* pstCoreUserInfo, struct BBOX_WRITE_FDS* pstFileFds) +{ + BBOX_NHDR stElfNhdr; + + if (NULL == pstCoreUserInfo || NULL == pstFileFds) { + bbox_print(PRINT_ERR, + "BBOX_WriteUserRegistersToFile parameters is invalid: pstCoreUserInfo or pstFileFds is NULL.\n"); + + return RET_ERR; + } + + errno_t rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); + securec_check_c(rc, "\0", "\0"); + + stElfNhdr.n_namesz = BBOX_CORE_NAME_LENGTH; + stElfNhdr.n_descsz = sizeof(struct BBOX_CORE_USER); + stElfNhdr.n_type = NT_PRXREG; + + if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || + BBOX_DoWrite(pstFileFds, (void*)BBOX_CORE_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH || + BBOX_DoWrite(pstFileFds, pstCoreUserInfo, sizeof(struct BBOX_CORE_USER)) != sizeof(struct BBOX_CORE_USER)) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); + + return RET_ERR; + } + + bbox_print(PRINT_LOG, + "Write UserRegisters size = %zd to the core file success.\n", + sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + sizeof(struct BBOX_CORE_USER)); + + return RET_OK; +} + +/* + * read /proc/self/auxv, write parts of it to a core file + * in : int iAuxvNum - Number of Auxv structures written to the core file + * struct BBOX_WRITE_FDS *pstWriteFds - pointer to core file. + * return RET_OK or RET_ERR. + */ +static int BBOX_WriteAuxvInfoToFile(int iAuxvNum, struct BBOX_WRITE_FDS* pstFileFds) +{ + int iAuxvFd = -1; + ssize_t iReadSize = -1; + unsigned int uCount = 0; + BBOX_NHDR stElfNhdr; + BBOX_AUXV_T stAuxv; + errno_t rc = EOK; + if (NULL == pstFileFds || iAuxvNum < 0) { + bbox_print(PRINT_ERR, + "BBOX_WriteAuxvInfoToFile parameters is invalid: pstFileFds may be NULL," \ + "iAuxvNum = %d.\n", + iAuxvNum); + + return RET_ERR; + } + + rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); + securec_check_c(rc, "\0", "\0"); + + stElfNhdr.n_namesz = BBOX_CORE_NAME_LENGTH; + stElfNhdr.n_descsz = iAuxvNum * sizeof(BBOX_AUXV_T); + stElfNhdr.n_type = NT_AUXV; + + if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || + BBOX_DoWrite(pstFileFds, (void*)BBOX_CORE_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); + + return RET_ERR; + } + + /* read /proc/self/Auxv, write to core file. */ + BBOX_NOINTR(iAuxvFd = sys_open(THREAD_SELF_AUXV_FILE, O_RDONLY, 0)); + if (iAuxvFd < 0) { + bbox_print(PRINT_ERR, "sys_open is failed, iAuxvFd = %d.\n", iAuxvFd); + + return RET_ERR; + } + + for (uCount = 0; uCount < (unsigned int)iAuxvNum; uCount++) { + iReadSize = -1; + rc = memset_s(&stAuxv, sizeof(BBOX_AUXV_T), 0, sizeof(BBOX_AUXV_T)); + securec_check_c(rc, "\0", "\0"); + + BBOX_NOINTR(iReadSize = sys_read(iAuxvFd, &stAuxv, sizeof(BBOX_AUXV_T))); + if (iReadSize != sizeof(BBOX_AUXV_T)) { + bbox_print(PRINT_ERR, "sys_read is failed, iReadSize = %zd.\n", iReadSize); + BBOX_NOINTR(sys_close(iAuxvFd)); + return RET_ERR; + } + if (sizeof(BBOX_AUXV_T) != BBOX_DoWrite(pstFileFds, &stAuxv, sizeof(BBOX_AUXV_T))) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); + BBOX_NOINTR(sys_close(iAuxvFd)); + return RET_ERR; + } + } + + BBOX_NOINTR(sys_close(iAuxvFd)); + + bbox_print(PRINT_LOG, + "Write Auxv size = %zd to the core file success.\n", + sizeof(BBOX_NHDR) + BBOX_CORE_STRING_LENGTH + stElfNhdr.n_descsz); + + return RET_OK; +} +/* + * Writes information of the structure that describes the state of a process to the core file + * in : struct BBOX_ELF_PRPSSTATUS *pstPrPsStatusInfo - A pointer to the structure that describes the state + * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file + * return RET_OK or RET_ERR. + */ +static int BBOX_WritePrPsStatusToFile(struct BBOX_ELF_PRPSSTATUS* pstPrPsStatusInfo, struct BBOX_WRITE_FDS* pstFileFds) +{ + BBOX_NHDR stElfNhdr; + int iResult = -1; + + if (NULL == pstPrPsStatusInfo || NULL == pstFileFds) { + bbox_print(PRINT_ERR, + "BBOX_WritePrPsStatusToFile parameters is invalid: pstPrPsStatusInfo or pstFileFds is NULL.\n"); + + return RET_ERR; + } + + errno_t rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); + securec_check_c(rc, "\0", "\0"); + + stElfNhdr.n_namesz = BBOX_CORE_NAME_LENGTH; + stElfNhdr.n_descsz = sizeof(struct BBOX_ELF_PRPSSTATUS); + stElfNhdr.n_type = NT_PRSTATUS; + + if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || + BBOX_DoWrite(pstFileFds, (void*)BBOX_CORE_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); + return RET_ERR; + } + + iResult = BBOX_DoWrite(pstFileFds, pstPrPsStatusInfo, sizeof(struct BBOX_ELF_PRPSSTATUS)); + if (iResult == RET_ERR || sizeof(struct BBOX_ELF_PRPSSTATUS) != iResult) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + + return RET_OK; +} + +/* + * Writes the FPU register contents to the core file + * in : struct BBOX_FPREGSET *pstFpRegisters - A pointer to an FPU register + * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file + * return RET_OK or RET_ERR. + */ +static int BBOX_WriteFpRegistersToFile(struct BBOX_FPREGSET* pstFpRegisters, struct BBOX_WRITE_FDS* pstFileFds) +{ +/* since ptrace() doesn't support to obtain float registers' context in aarch64, don't dump it out. */ +#if !defined(__aarch64__) + BBOX_NHDR stElfNhdr; + + if (NULL == pstFpRegisters || NULL == pstFileFds) { + bbox_print(PRINT_ERR, + "BBOX_WriteFpRegistersToFile parameters is invalid: pstFpRegisters or pstFileFds is NULL.\n"); + return RET_ERR; + } + + errno_t rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); + securec_check_c(rc, "\0", "\0"); + + stElfNhdr.n_namesz = BBOX_CORE_NAME_LENGTH; + stElfNhdr.n_descsz = sizeof(struct BBOX_FPREGSET); + stElfNhdr.n_type = NT_FPREGSET; + + if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || + BBOX_DoWrite(pstFileFds, (void*)BBOX_CORE_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH || + BBOX_DoWrite(pstFileFds, pstFpRegisters, sizeof(struct BBOX_FPREGSET)) != sizeof(struct BBOX_FPREGSET)) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); + return RET_ERR; + } +#endif + + return RET_OK; +} + +/* + * Writes the SSE structure contents to the core file + * in : struct BBOX_FPXREGSET *pstFpxRegisters - A pointer to an SSE structure + * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file + * return RET_OK or RET_ERR. + */ +static int BBOX_WriteFpxRegistersToFile(struct BBOX_FPXREGSET* pstFpxRegisters, struct BBOX_WRITE_FDS* pstFileFds) +{ + BBOX_NHDR stElfNhdr; + + if (NULL == pstFpxRegisters || NULL == pstFileFds) { + bbox_print(PRINT_ERR, + "BBOX_WriteFpxRegistersToFile parameters is invalid: pstFpxRegisters or pstFileFds is NULL.\n"); + return RET_ERR; + } + + errno_t rc = memset_s(&stElfNhdr, sizeof(BBOX_NHDR), 0, sizeof(BBOX_NHDR)); + securec_check_c(rc, "\0", "\0"); + + stElfNhdr.n_namesz = BBOX_LINUX_NAME_LENGTH; + stElfNhdr.n_descsz = sizeof(struct BBOX_FPXREGSET); + stElfNhdr.n_type = NT_PRXFPREG; + + if (BBOX_DoWrite(pstFileFds, &stElfNhdr, sizeof(BBOX_NHDR)) != sizeof(BBOX_NHDR) || + BBOX_DoWrite(pstFileFds, (void*)BBOX_LINUX_STRING, BBOX_CORE_STRING_LENGTH) != BBOX_CORE_STRING_LENGTH || + BBOX_DoWrite(pstFileFds, pstFpxRegisters, sizeof(struct BBOX_FPXREGSET)) != sizeof(struct BBOX_FPXREGSET)) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed.\n"); + return RET_ERR; + } + + return RET_OK; +} + +/* + * Writes the prpsstatus information and the register information for the process to the core file + * in : int iFpxRegistersFlag - Whether to write Fpx register information token + * struct BBOX_THREAD_NOTE_INFO *pstThreadPrPsStatusInfo - A pointer to the structure of thread note segment + * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file + * return RET_OK or RET_ERR. + */ +static int BBOX_WriteThreadNoteToFile( + int iFpxRegistersFlag, struct BBOX_THREAD_NOTE_INFO* pstThreadPrPsStatusInfo, struct BBOX_WRITE_FDS* pstFileFds) +{ + int iResult = RET_ERR; + + if (NULL == pstThreadPrPsStatusInfo || NULL == pstFileFds) { + bbox_print(PRINT_ERR, + "BBOX_WriteThreadNoteToFile parameters is invalid: pstThreadPrPsStatusInfo or pstFileFds is NULL.\n"); + return RET_ERR; + } + + /* Write CPU register information to core file */ + iResult = BBOX_WritePrPsStatusToFile(&(pstThreadPrPsStatusInfo->stPrpsstatus), pstFileFds); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WritePrPsStatusToFile is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + + /* Write FPU register information to core file */ + iResult = BBOX_WriteFpRegistersToFile(&(pstThreadPrPsStatusInfo->stFpRegisters), pstFileFds); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteFpRegistersToFile is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + + if (BBOX_TRUE == iFpxRegistersFlag) { + /* Write SSE information into core file if exist. */ + iResult = BBOX_WriteFpxRegistersToFile(&(pstThreadPrPsStatusInfo->stFpxRegisters), pstFileFds); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteFpxRegistersToFile is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + } + + return RET_OK; +} + +/* + * To align, write 0 to the core file + * in : size_t uiNoteAlign - The number of bytes that need to be written to the file for alignment + * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file + */ +static int BBOX_CoreFileAlignToPage(size_t uiNoteAlign, struct BBOX_WRITE_FDS* pstFileFds) +{ + ssize_t iWriteSize = -1; + size_t iDateSize = 0; + char acNoteAlign[BBOX_BUFF_LITTLE_SIZE]; + + if (NULL == pstFileFds) { + bbox_print(PRINT_ERR, "BBOX_CoreFileAlignToPage parameters is invalid: pstFileFds is NULL.\n"); + return RET_ERR; + } + + while (uiNoteAlign > 0) { + if (uiNoteAlign > sizeof(acNoteAlign)) { + iDateSize = sizeof(acNoteAlign); + uiNoteAlign -= sizeof(acNoteAlign); + } else { + iDateSize = uiNoteAlign; + uiNoteAlign = 0; + } + + errno_t rc = memset_s(acNoteAlign, BBOX_BUFF_LITTLE_SIZE, 0, sizeof(acNoteAlign)); + securec_check_c(rc, "\0", "\0"); + + iWriteSize = BBOX_DoWrite(pstFileFds, acNoteAlign, iDateSize); + if (iWriteSize == RET_ERR || iDateSize != (size_t)iWriteSize) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iWriteSize = %zd.\n", iWriteSize); + + return RET_ERR; + } + } + + bbox_print(PRINT_LOG, "Write Note align size = %zu to the core file success.\n", uiNoteAlign); + + return RET_OK; +} + +/* + * write Note segment into core file. + * in : struct BBOX_ELF_NOTE_INFO *pstNoteInfo - A pointer to the structure of the note segment + * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to the structure of the core file + */ +static int BBOX_WriteNoteInfo(struct BBOX_ELF_NOTE_INFO* pstNoteInfo, struct BBOX_WRITE_FDS* pstFileFds) +{ + unsigned int uCount = 0; + int iResult = RET_ERR; + + if (NULL == pstNoteInfo || NULL == pstFileFds) { + bbox_print(PRINT_ERR, + "BBOX_WriteNoteInfo parameters is invalid: pstNoteInfo or pstFileFds is NULL.\n"); + return RET_ERR; + } + + int iThreadNum = pstNoteInfo->iThreadNoteInfoNum; + struct BBOX_THREAD_NOTE_INFO* pstThreadPrPsStatusInfo = pstNoteInfo->pstThreadNoteInfo; + struct BBOX_ELF_PRPSINFO* pstPrPsInfo = &(pstNoteInfo->stPrpsinfo); + struct BBOX_CORE_USER* pstCoreUserInfo = &(pstNoteInfo->stCoreUser); + + /* write BBOX_ELF_PRPSINFO into core file */ + iResult = BBOX_WritePrPsinfoToFile(pstPrPsInfo, pstFileFds); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WritePrPsinfoToFile is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + + /* write user data into core file */ + iResult = BBOX_WriteUserRegistersToFile(pstCoreUserInfo, pstFileFds); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteUserRegistersToFile is failed, iResult = %d.\n", iResult); + return RET_ERR; + } + + if (pstNoteInfo->iAuxvNoteInfoNum) { + /* write Auxv into core */ + iResult = BBOX_WriteAuxvInfoToFile(pstNoteInfo->iAuxvNoteInfoNum, pstFileFds); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteAuxvInfoToFile is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + } + + for (uCount = 0; uCount < (unsigned int)iThreadNum; uCount++) { + if ((pstThreadPrPsStatusInfo + uCount)->stPrpsstatus.tpid == pstNoteInfo->tMainPid) { + /* write primary process register information into core file. */ + iResult = BBOX_WriteThreadNoteToFile( + pstNoteInfo->iFpxRegistersFlag, (pstThreadPrPsStatusInfo + uCount), pstFileFds); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteThreadNoteToFile is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + } + } + + for (uCount = 0; uCount < (unsigned int)iThreadNum; uCount++) { + if ((pstThreadPrPsStatusInfo + uCount)->stPrpsstatus.tpid != pstNoteInfo->tMainPid) { + /* write all non-primary process register information into core file. */ + iResult = BBOX_WriteThreadNoteToFile( + pstNoteInfo->iFpxRegistersFlag, (pstThreadPrPsStatusInfo + uCount), pstFileFds); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteThreadNoteToFile is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + } + } + + bbox_print(PRINT_LOG, "Write Prpsstatus and Registers to the core file success.\n"); + + /* align the Note segment */ + iResult = BBOX_CoreFileAlignToPage(pstNoteInfo->uiNoteAlign, pstFileFds); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_CoreFileAlignToPage is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + bbox_print(PRINT_LOG, "Write Note info to the core file success.\n"); + + return RET_OK; +} + +/* + * read content of address space, and write it into core file. + * in : struct BBOX_VM_MAPS *pstVmMappingSegment - A pointer to the structure of the mapping segment + * int iVmMappingNum - The number of mapping segments in the address space + * union BBOX_VM_VDSO *pstVmVDSO - A pointer to the structure of the VDSO segment + * struct BBOX_WRITE_FDS *pstWriteFds - A pointer to core file + * return RET_OK or RET_ERR + */ +static int BBOX_WriteElfVmToFile(struct BBOX_VM_MAPS* pstVmMappingSegment, int iVmMappingNum, + union BBOX_VM_VDSO* pstVmVDSO, struct BBOX_WRITE_FDS* pstFileFds) +{ + unsigned int uCount = 0; + size_t uiStartAddress = 0; + size_t uiWriteSize = 0; + ssize_t iResult = RET_ERR; + + if (NULL == pstVmMappingSegment || iVmMappingNum < 0 || NULL == pstFileFds || NULL == pstVmVDSO) { + bbox_print(PRINT_ERR, + "BBOX_WriteElfVmToFile parameters is invalid: iVmMappingNum = %d, " \ + "pstVmMappingSegment, pstFileFds or pstVmVDSO is NULL.\n", + iVmMappingNum); + + return RET_ERR; + } + + /* read content from start address to end address in address space, and write it into core file. */ + for (uCount = 0; uCount < (unsigned int)iVmMappingNum; uCount++) { + uiStartAddress = pstVmMappingSegment[uCount].uiStartAddress; + uiWriteSize = pstVmMappingSegment[uCount].uiWriteSize; + + if (pstVmMappingSegment[uCount].iIsRemoveFlags == BBOX_FALSE && uiWriteSize > 0) { + iResult = BBOX_DoWrite(pstFileFds, (void*)uiStartAddress, uiWriteSize); + if ((iResult == RET_ERR) || (iResult != (ssize_t)uiWriteSize)) { + bbox_print(PRINT_ERR, "BBOX_DoWrite parameters is failed, iWriteSize = %zu.\n", uiWriteSize); + + return RET_ERR; + } + + bbox_print( + PRINT_DBG, "pstVmMappingSegment[%u] : write size = %zu to the core file.\n", uCount, uiWriteSize); + } + } + + if (pstVmVDSO->uiVDSOAddress) { + BBOX_PHDR* pstVDSOPhdr = (BBOX_PHDR*)(pstVmVDSO->uiVDSOAddress + pstVmVDSO->pVDSOEhdr->e_phoff); + for (uCount = 0; uCount < pstVmVDSO->pVDSOEhdr->e_phnum; uCount++) { + /* wirte VDSO information into core file */ + BBOX_PHDR* pstVDSOTempPhdr = pstVDSOPhdr + uCount; + if (PT_LOAD != pstVDSOTempPhdr->p_type) { + iResult = BBOX_DoWrite(pstFileFds, (void*)pstVDSOTempPhdr->p_vaddr, pstVDSOTempPhdr->p_filesz); + if ((iResult == RET_ERR) || (iResult != (ssize_t)(pstVDSOTempPhdr->p_filesz))) { + bbox_print(PRINT_ERR, "BBOX_DoWrite is failed, iResult = %zd.\n", iResult); + return RET_ERR; + } + + bbox_print(PRINT_DBG, "VDSO[%u] : write size = %zd to the core file.\n", uCount, iResult); + } + } + } + + bbox_print(PRINT_LOG, "Write Vm mapping segment to the core file success.\n"); + + return RET_OK; +} + +/* + * write all segment into core file, include NOTE, mapping, VDSO + * in : struct BBOX_ELF_NOTE_INFO *pstNoteInfo - A pointer to the structure of the note segment + * struct BBOX_VM_MAPS *pstVmMappingSegment - A pointer to the structure of the mapping segment + * int iSegmentNum - The number of mapping segments in the address space + * union BBOX_VM_VDSO *pstVmVDSO - A pointer to the structure of the VDSO segment + * struct BBOX_WRITE_FDS *pstFileWriteFd - A pointer to core file + */ +static int BBOX_WriteElfSegment(struct BBOX_ELF_NOTE_INFO* pstNoteInfo, struct BBOX_VM_MAPS* pstVmMappingSegment, + int iSegmentNum, union BBOX_VM_VDSO* pstVmVDSO, struct BBOX_WRITE_FDS* pstFileWriteFd) +{ + int iResult = RET_ERR; + + if (NULL == pstNoteInfo || NULL == pstVmMappingSegment || NULL == pstVmVDSO || NULL == pstFileWriteFd || + iSegmentNum <= 0) { + bbox_print(PRINT_ERR, + "BBOX_WriteElfSegment parameters is invalid: pstNoteInfo, pstVmMappingSegment, " \ + "pstVmVDSO or pstFileWriteFd is NULL, iSegmentNum = %d.\n", + iSegmentNum); + + return RET_ERR; + } + + /* write Note segment into core file */ + iResult = BBOX_WriteNoteInfo(pstNoteInfo, pstFileWriteFd); + if (RET_OK != iResult) { + + bbox_print(PRINT_ERR, "BBOX_WriteNoteInfo is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + /* write process address space information into core file. */ + iResult = BBOX_WriteElfVmToFile(pstVmMappingSegment, iSegmentNum, pstVmVDSO, pstFileWriteFd); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteElfVmToFile is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + return RET_OK; +} + +/* + * calculate end time of coredump and print the time coredump take. + */ +static void BBOX_CalculateUsedTime(void) +{ + long int iCoreDumpEndTime = 0; + struct kernel_timeval stProgramCoreDumpTime = {0}; + + /* calculate time. */ + sys_gettimeofday(&stProgramCoreDumpTime, NULL); + iCoreDumpEndTime = stProgramCoreDumpTime.tv_sec; + + bbox_print(PRINT_TIP, "Coredump probably end at %ld\n", stProgramCoreDumpTime.tv_sec); + bbox_print(PRINT_TIP, "Coredump used time: %ld sec\n", iCoreDumpEndTime - g_iCoreDumpBeginTime); + bbox_print(PRINT_LOG, "Get information success.\n"); + bbox_print(PRINT_LOG, "Create core file success.\n"); +} + +/* + * Write all section into core file. + * in : struct BBOX_ELF_SECTION *pstElfSectionInfo - pointer to the structure of a section + * struct BBOX_WRITE_FDS *pstFileWriteFd - pointer to the core file + * return RET_OK or RET_ERR + */ +static int BBOX_WriteElfSection(struct BBOX_ELF_SECTION* pstElfSectionInfo, struct BBOX_WRITE_FDS* pstFileFds) +{ + unsigned int uiCount = 0; + BBOX_SECTION_STRU* pstSection = NULL; + + /* print time and end information of core file. */ + BBOX_CalculateUsedTime(); + + /* Write section into core file. */ + for (uiCount = 0; uiCount < pstElfSectionInfo->uiSectionNum; uiCount++) { + pstSection = pstElfSectionInfo->pstSection + uiCount; + BBOX_WRITE(pstFileFds, pstSection->pacSectionDesc, pstSection->uiSectionDescSize); + } + + bbox_print(PRINT_LOG, "Write all section to fill successed.\n"); + + return RET_OK; +} + +/* + * close core file + * in : struct BBOX_WRITE_FDS *pstFileWriteFd - Pointer to a structure that describes the properties of a core file. + * return RET_OK or RET_ERR + */ +static int BBOX_CloseCoreFile(struct BBOX_WRITE_FDS* pstFileWriteFd) +{ + if (NULL == pstFileWriteFd) { + bbox_print(PRINT_ERR, "BBOX_CloseCoreFile parameters is invalid: pstFileWriteFd is NULL.\n"); + + return RET_ERR; + } + + if (pstFileWriteFd->iWriteFd >= 0) { + sys_pclose(pstFileWriteFd->iWriteFd); + pstFileWriteFd->iWriteFd = -1; + } + + bbox_print(PRINT_LOG, "Close core file success.\n"); + return RET_OK; +} + +/* + * create elf core file. + * in : BBOX_GetAllThreadDone pDone - The callback function for thawing + * void *pDoneHandle - The callback function parameter to be thawed + * int iNumThreads - The number of processes, that is, the length of the process number array + * pid_t *pPids - pointer to execute the process number array + * va_list ap - Multiparameter list + * return RET_OK or RET_ERR + */ + /* + 该函数的功能是生成一个包含各种信息的core文件。它的参数包括回调函数指针、句柄、线程数量、进程ID数组以及可变参数列表。 + +该函数首先检查参数的有效性,如果参数无效,返回RET_ERR。 + +然后使用原子变量锁进入一个循环,等待直到可以将锁变量加1。在循环内部,打印一个调试信息,并让函数休眠1秒。 + +退出循环后,声明一个大小为线程数量的BBOX_THREAD_NOTE_INFO结构体数组,并从可变参数列表中获取堆栈帧。如果堆栈帧为NULL,则打印错误信息并返回RET_ERR。 + +接下来,初始化几个结构体和变量,包括BBOX_VM_VDSO、BBOX_ELF_NOTE_INFO和BBOX_WRITE_FDS。设置主进程ID和线程信息数。 + +然后调用BBOX_GetVmMapsNum函数获取/proc/self/maps文件的行数,表示段的数量。如果数量小于等于0,则打印错误信息并返回RET_ERR。 + +接下来,声明一个大小为段数量加上一个常量值的BBOX_VM_MAPS结构体数组,并初始化该数组。然后调用BBOX_FillAllInfoOfCoreFile函数来填充生成core文件所需的所有信息。如果返回值不是RET_OK,则打印错误信息并返回RET_ERR。 + +之后,调用回调函数通知线程模块数据获取已完成。获取core文件名并检查其有效性。如果无效,则打印错误信息并返回RET_ERR。 + +接下来,调用BBOX_OpenCoreFile函数创建core文件。如果返回值不是RET_OK,则打印错误信息并跳转到ERR标签。 + +在ERR标签处,减少原子变量锁的值,并检查是否需要调用BBOX_CloseCoreFile函数。如果需要,如果返回值不是RET_OK,则打印错误信息。 + +最后,返回BBOX_CloseCoreFile函数的结果,该结果是函数的返回值或RET_ERR,具体取决于函数的返回值。 + */ +int BBOX_DoDumpElfCore(BBOX_GetAllThreadDone pDone, void* pDoneHandle, int iNumThreads, pid_t* ptPids, va_list ap) +{ + int iSegmentNum = -1; + int iResult = RET_ERR; + int iCloseFile = RET_ERR; + int iPhdrSum = 0; + pid_t tMainPid = 0; + Frame* pFrame = NULL; + char* pFileName = NULL; + union BBOX_VM_VDSO stVmVDSO; + struct BBOX_ELF_NOTE_INFO stNoteInfo; + struct BBOX_WRITE_FDS stFileWriteFd; + errno_t rc = EOK; + + if (NULL == ptPids || NULL == pDone || NULL == pDoneHandle || iNumThreads <= 0) { + bbox_print(PRINT_ERR, + "BBOX_CloseCoreFile parameters is invalid: pDone, ptPids or pDoneHandle is NULL, " \ + "iNumThreads = %d.\n", + iNumThreads); + + return RET_ERR; + } + + /* Atomic variable lock to prevent reentry. */ + while (BBOX_AtomicIncReturn(&g_stLockBlackList) > 1) { + BBOX_AtomicDec(&g_stLockBlackList); + bbox_print(PRINT_DBG, "add blacklist addr is running, waiting.\n"); + sleep(1); + } + + struct BBOX_THREAD_NOTE_INFO astThreadNoteInfo[iNumThreads]; + + pFrame = (Frame*)va_arg(ap, Frame*); + if (NULL == pFrame) { + bbox_print(PRINT_ERR, "Get stack frame failed.\n"); + BBOX_AtomicDec(&g_stLockBlackList); + return RET_ERR; + } + + rc = memset_s(&stVmVDSO, sizeof(union BBOX_VM_VDSO), 0, sizeof(union BBOX_VM_VDSO)); + securec_check_c(rc, "\0", "\0"); + rc = memset_s(&stNoteInfo, sizeof(struct BBOX_ELF_NOTE_INFO), 0, sizeof(struct BBOX_ELF_NOTE_INFO)); + securec_check_c(rc, "\0", "\0"); + rc = memset_s(&stFileWriteFd, sizeof(struct BBOX_WRITE_FDS), 0, sizeof(struct BBOX_WRITE_FDS)); + securec_check_c(rc, "\0", "\0"); + rc = memset_s(astThreadNoteInfo, + iNumThreads * sizeof(struct BBOX_THREAD_NOTE_INFO), + 0, + iNumThreads * sizeof(struct BBOX_THREAD_NOTE_INFO)); + securec_check_c(rc, "\0", "\0"); + + stNoteInfo.pstThreadNoteInfo = astThreadNoteInfo; + stNoteInfo.iThreadNoteInfoNum = iNumThreads; /* count of thread */ + stVmVDSO.uiVDSOAddress = 0; + tMainPid = pFrame->tid; + stNoteInfo.tMainPid = tMainPid; + + /* Get count of line in /proc/self/maps, it also is segment num. */ + iSegmentNum = BBOX_GetVmMapsNum(); + if (iSegmentNum <= 0) { + bbox_print(PRINT_ERR, "BBOX_GetVmMapsNum is invald iSegmentNum = %d\n", iSegmentNum); + BBOX_AtomicDec(&g_stLockBlackList); + return RET_ERR; + } + + bbox_print(PRINT_LOG, "Get Vm mapping number success, iSegmentNum = %d\n", iSegmentNum); + + /* define variable to record start address, end address, jurisdiction, offset and so on of segment in /proc/self/maps */ + struct BBOX_VM_MAPS astVmMappingSegment[iSegmentNum + BBOX_EXTERN_VM_MAX]; + + rc = memset_s(astVmMappingSegment, + (iSegmentNum + BBOX_EXTERN_VM_MAX) * sizeof(struct BBOX_VM_MAPS), + 0, + (iSegmentNum + BBOX_EXTERN_VM_MAX) * sizeof(struct BBOX_VM_MAPS)); + securec_check_c(rc, "\0", "\0"); + + /* Fill BBOX_VM_MAPS, BBOX_ELF_NOTE_INFO, BBOX_VM_VDSO, and write them into core file. */ + iResult = BBOX_FillAllInfoOfCoreFile( + pFrame, astVmMappingSegment, &iSegmentNum, &iPhdrSum, ptPids, &stNoteInfo, &stVmVDSO); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_FillAllInfoOfCoreFile is failed, iResult = %d.\n", iResult); + BBOX_AtomicDec(&g_stLockBlackList); + return RET_ERR; + } + + /* Notifie the thread module that the data retrieval is complete. */ + pDone(pDoneHandle); + + pFileName = (char*)va_arg(ap, char*); + if (CheckFilenameValid(pFileName) == RET_ERR) { + bbox_print(PRINT_ERR, "check core file name failed\n"); + return RET_ERR; + } + iResult = BBOX_OpenCoreFile(pFrame, pFileName, &stFileWriteFd); /* create core file. */ + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_OpenCoreFile is failed, iResult = %d.\n", iResult); + + goto ERR; + } + + /* fill core file header and write it into core file. */ + iResult = BBOX_WriteElfEhdr(iPhdrSum, &stFileWriteFd); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteElfEhdr is failed, iResult = %d.\n", iResult); + goto ERR; + } + + /* Fill ElfPhdr and write it into core file. */ + iResult = BBOX_WriteElfHdr(iPhdrSum, &stNoteInfo, iSegmentNum, &stVmVDSO, astVmMappingSegment, &stFileWriteFd); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteElfPhdr is failed, iResult = %d.\n", iResult); + goto ERR; + } + + /* Write Note segment and process address content into core file. */ + iResult = BBOX_WriteElfSegment(&stNoteInfo, astVmMappingSegment, iSegmentNum, &stVmVDSO, &stFileWriteFd); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteElfSegment is failed, iResult = %d.\n", iResult); + + goto ERR; + } + + /* Write BBOX_ELF_PRPSINFO into core file */ + iResult = BBOX_WriteElfSection(&g_stElfSectionInfo, &stFileWriteFd); + if (RET_OK != iResult) { + bbox_print(PRINT_ERR, "BBOX_WriteElfSection is failed, iResult = %d.\n", iResult); + + goto ERR; + } + + bbox_print(PRINT_LOG, "Create core file success.\n"); + +ERR: + BBOX_AtomicDec(&g_stLockBlackList); + + iCloseFile = BBOX_CloseCoreFile(&stFileWriteFd); + if (RET_OK != iCloseFile) { + bbox_print(PRINT_ERR, "BBOX_CloseCoreFile is failed, iCloseFile = %d.\n", iCloseFile); + } + + return (iCloseFile == RET_OK) ? iResult : RET_ERR; +} + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ -- 2.34.1 From a4969dddbcc2fb9006f06fc6c9c358761ab7b351 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:18:57 +0800 Subject: [PATCH 22/56] Delete 'src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp' --- .../cbb/bbox/bbox_elf_dump_base.cpp | 686 ------------------ 1 file changed, 686 deletions(-) delete mode 100644 src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp b/src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp deleted file mode 100644 index c4f965609..000000000 --- a/src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp +++ /dev/null @@ -1,686 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * bbox_elf_dump_base.cpp - * - * IDENTIFICATION - * src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp - * - * ------------------------------------------------------------------------- - */ -#include "bbox.h" -#include "bbox_elf_dump_base.h" -#include "bbox_syscall_support.h" -#include "../../src/include/securec.h" -#include "../../src/include/securec_check.h" - -#ifdef __cplusplus -#if __cplusplus -extern "C" { -#endif -#endif /* __cplusplus */ - -/* black list of atomic variable */ -BBOX_ATOMIC_STRU g_stLockBlackList = BBOX_ATOMIC_INIT(0); - -/* the length of black list */ -static int g_iNumBlackList = 0; - -/* the lookup position of black list */ -static int g_iPosBlackList = 0; - -/* array store for black list */ -static BBOX_BLACKLIST_STRU g_stBlackList[BBOX_BLACK_LIST_COUNT_MAX]; - - /* -function name: BBOX_DetermineMsb -description: The function should judge the mode that PC uses to store data is Big-endian/Little-endian. -arguments: void -return value: An integer that indicates the mode is Big-endian/Little-endian, - if it is ELFDATA2LSB, the mode is Little-endian, - if it is ELFDATA2MSB, the mode is Big-endian. -note锛歍he way that this function judge the mode that PC uses to store data is through a union variable unProbe, - at first we give its first member variable sShortInt a value BBOX_MSB_LSB_INT of type short, then its second - member variable cSplit[sizeof(short)] equaling to cSplit[2] would have the equal value of the first. Finally we - just need to compare BBOX_LITTER_BITS and BBOX_HIGH_BITS, namely the low byte and high byte of - BBOX_MSB_LSB_INT, with unProbe.cSplit[0] and unProbe.cSplit[1], if they are correspondingly equal, the mode is - Little-endian, else is the Big-endian. -date: 2022/8/2 -contact tel: 18720816902 - */ -int BBOX_DetermineMsb(void) -{ - union INT_PROBE { - short sShortInt; - char cSplit[sizeof(short)]; - } unProbe; - - unProbe.sShortInt = BBOX_MSB_LSB_INT; - - if ((BBOX_LITTER_BITS == unProbe.cSplit[0]) && (BBOX_HIGH_BITS == unProbe.cSplit[1])) { - return ELFDATA2LSB; - } else { - return ELFDATA2MSB; - } -} - -/* - * converts a string to time - * in : char *pSwitch - the string to be converted - * out : struct BBOX_ELF_TIMEVAL *pstElfTimeval - result - * return : RET_OK - success - * RET_ERR - failed - */ -int BBOX_StringToTime(const char* pSwitch, struct BBOX_ELF_TIMEVAL* pstElfTimeval) -{ - int iSwitchTimes = 0; - - if (NULL == pSwitch || NULL == pstElfTimeval) { - bbox_print(PRINT_ERR, - "BBOX_StringToTime parameters is invalid: pSwitch or pstElfTimeval is NULL.\n"); - return RET_ERR; - } - - /* converts a string to num */ - while (*pSwitch && *pSwitch != ' ') { - iSwitchTimes = DECIMALISM_SPAN * iSwitchTimes + (*pSwitch) - '0'; - pSwitch++; - } - - pstElfTimeval->lTvSec = iSwitchTimes / SEC_CHANGE_MIRCO_SEC; - pstElfTimeval->lTvMicroSec = (iSwitchTimes % SEC_CHANGE_MIRCO_SEC) * SEC_CHANGE_MIRCO_SEC; - - return RET_OK; -} - -/* - * read a character from file - * in : struct BBOX_READ_FILE_IO *pstIO - bbox file pointer - * return : the character read in file - success - * RET_ERR - failed - */ -int BBOX_GetCharFromFile(struct BBOX_READ_FILE_IO* pstIO) -{ - ssize_t iReadSize = -1; - - if (NULL == pstIO) { - bbox_print(PRINT_ERR, "BBOX_GetCharFromFile parameters is invalid: pstIO is NULL.\n"); - return RET_ERR; - } - - unsigned char* pTempIO = pstIO->pData; - if (pTempIO == pstIO->pEnd) { - /* read character from file when the buffer is empty, and push it into buffer */ - BBOX_NOINTR(iReadSize = sys_read(pstIO->iFd, pstIO->szBuff, sizeof(pstIO->szBuff))); - if (iReadSize <= 0) { - if (0 == iReadSize) { - errno = 0; - } - - return RET_ERR; - } - - pTempIO = &(pstIO->szBuff[0]); - pstIO->pEnd = &(pstIO->szBuff[iReadSize]); - } - - pstIO->pData = pTempIO + 1; - - return *pTempIO; -} - -/* - * converts a string to num - * in : struct BBOX_READ_FILE_IO *pstIO - file to be read - * out : size_t *pAddress - buffer to store result - * return : the result num - success - * RET_ERR - failed - */ -int BBOX_StringSwitchInt(struct BBOX_READ_FILE_IO* pstIO, size_t* pAddress) -{ - int iMappingTextChar = 0; - - if (NULL == pstIO || NULL == pAddress) { - bbox_print( - PRINT_ERR, "BBOX_StringSwitchInt parameters is invalid: pstIO or pAddress is NULL.\n"); - return RET_ERR; - } - - *pAddress = 0; - iMappingTextChar = BBOX_GetCharFromFile(pstIO); - while ( - (iMappingTextChar >= '0' && iMappingTextChar <= '9') || (iMappingTextChar >= 'a' && iMappingTextChar <= 'f')) { - - /* left shift the variable, and add the num converted from character at the end. */ - *pAddress = - (*pAddress << ONE_HEXA_DECIMAL_BITS) | - (unsigned int)(iMappingTextChar < 'A' ? iMappingTextChar - '0' - : ((unsigned int)iMappingTextChar & 0xF) + ASC2_CHAR_GREATER_NUM); - iMappingTextChar = BBOX_GetCharFromFile(pstIO); /* read next character */ - } - - return iMappingTextChar; -} - -/* - * when read file /proc/self/maps, ignore unusefull information and skip to the end of line - * after we have geting all necessary information. - * return : count of character store into buffer - success - * RET_ERR - failed - */ -int BBOX_SkipToLineEnd(struct BBOX_READ_FILE_IO* pstReadIO) -{ - int iGetChar = -1; - - if (NULL == pstReadIO) { - bbox_print(PRINT_ERR, "BBOX_SkipToLineEnd parameters is invalid: pstReadIO is NULL.\n"); - return RET_ERR; - } - - do { - /* reads characters until the newline character */ - iGetChar = BBOX_GetCharFromFile(pstReadIO); - if (RET_ERR == iGetChar) { - bbox_print(PRINT_ERR, "BBOX_SkipToLineEnd is failed, iGetChar= %d.\n", iGetChar); - return RET_ERR; - } - } while (iGetChar != '\n'); - - return RET_OK; -} - -/* - * judge whether the range between *pStartAddress* and *pEndAddress* is in black list or not. - * If found, return the blacklist item which cover it, else return NULL. - * - * NOTE: this function is a thread-unsafe function since it works as an iterator. - */ -BBOX_BLACKLIST_STRU *_BBOX_FindAddrInBlackList(const void *pStartAddress, const void *pEndAddress) -{ - int i = 0; - int iPerformance = 0; - size_t uiPageSize = sys_sysconf(_SC_PAGESIZE); - - /* if the cursor reachs the end of blacklist, start a new trip. */ - if (g_iPosBlackList >= g_iNumBlackList) { - g_iPosBlackList = 0; - } - - for (i = g_iPosBlackList; i < g_iNumBlackList; i++) { - iPerformance++; - - /* - * if the endAddress of segemnt is less than the startAddress of this item, it means there is - * no cross with the rest blacklist items since blacklist items are in increasing order. - */ - if (pEndAddress <= g_stBlackList[i].pBlackStartAddr) { - bbox_print(PRINT_DBG, "\nFIND BL BY %d TIMES, POS = %d.\n\n", iPerformance, g_iPosBlackList); - bbox_print(PRINT_DBG, "not find black list in segment.\n"); - return NULL; - } - - if (pStartAddress <= g_stBlackList[i].pBlackStartAddr && - g_stBlackList[i].pBlackStartAddr < pEndAddress) { - g_iPosBlackList = i; - bbox_print(PRINT_DBG, "\nFIND BL BY %d TIMES, POS = %d.\n\n", iPerformance, g_iPosBlackList); - bbox_print(PRINT_DBG, "find black list in segment.\n"); - return &(g_stBlackList[i]); - } - - if (pStartAddress < g_stBlackList[i].pBlackEndAddr && - g_stBlackList[i].pBlackEndAddr <= pEndAddress) { - if (((uintptr_t)pEndAddress - (uintptr_t)(g_stBlackList[i].pBlackEndAddr)) < uiPageSize) { - bbox_print(PRINT_DBG, "find black list in segment, but size < 4K, do not care return.\n"); - return NULL; - } else { - g_iPosBlackList = i; - bbox_print(PRINT_DBG, "\nFIND BL BY %d TIMES, POS = %d.\n\n", iPerformance, g_iPosBlackList); - bbox_print(PRINT_DBG, "find black list in segment.\n"); - return &(g_stBlackList[i]); - } - } - } - - bbox_print(PRINT_DBG, "\nFIND BL BY %d TIMES, POS = %d.\n\n", iPerformance, g_iPosBlackList); - bbox_print(PRINT_DBG, "not find black list in segment.\n"); - - /* no cross between this segment and blacklist items. */ - return NULL; -} - -/* - * set a blaclist item to drop it from core file. - * void *pAddress : the head address of excluded memory - * unsigned long long uiLen : memory size - * return RET_OK if success else RET_ERR. - */ -int _BBOX_AddBlackListAddress(void* pAddress, unsigned long long uiLen) -{ - unsigned int uiFound = 0; - - if (pAddress == NULL || uiLen < BBOX_BLACK_LIST_MIN_LEN) { - bbox_print(PRINT_ERR, "parameter uiLen(%llu) is invaild.\n", uiLen); - return RET_ERR; - } - - /* use atomic increment to control concurrency. */ - while (BBOX_AtomicIncReturn(&g_stLockBlackList) > 1) { - BBOX_AtomicDec(&g_stLockBlackList); - bbox_print(PRINT_DBG, "add blacklist addr is running, waiting.\n"); - sleep(1); - } - - /* if too many blacklist items were added, return error while its upper limits reaches. */ - if (g_iNumBlackList >= BBOX_BLACK_LIST_COUNT_MAX) { - BBOX_AtomicDec(&g_stLockBlackList); - bbox_print(PRINT_ERR, "blacklist addr total reach max, failed.\n"); - return RET_ERR; - } - - /* - * suppose that address became bigger and bigger, move forward from blacklist's tail, - * and find the proper postion to insert this address into the blacklist. - */ - for (int i = g_iNumBlackList - 1; i >= 0; i--) { - /* if try to add the same address again, report error. */ - if (g_stBlackList[i].pBlackStartAddr == pAddress) { - BBOX_AtomicDec(&g_stLockBlackList); - - if (g_stBlackList[i].uiLength == uiLen) { - return RET_OK; - } - - bbox_print(PRINT_ERR, "add addr has in blacklist\n"); - return RET_ERR; - } - - if (g_stBlackList[i].pBlackStartAddr > pAddress) { - g_stBlackList[i+1].pBlackStartAddr = g_stBlackList[i].pBlackStartAddr; - g_stBlackList[i+1].pBlackEndAddr = g_stBlackList[i].pBlackEndAddr; - g_stBlackList[i+1].uiLength = g_stBlackList[i].uiLength; - } else { - g_stBlackList[i+1].pBlackStartAddr = pAddress; - g_stBlackList[i+1].uiLength = uiLen; - g_stBlackList[i+1].pBlackEndAddr= (void *)((char *)pAddress + uiLen); - uiFound = 1; - break; - } - } - - /* if no found, it means this address is smaller than all锛宲ut it in the head. */ - if (uiFound == 0) { - g_stBlackList[0].pBlackStartAddr = pAddress; - g_stBlackList[0].uiLength = uiLen; - g_stBlackList[0].pBlackEndAddr= (void *)((char *)pAddress + uiLen); - } - - ++g_iNumBlackList; - - BBOX_AtomicDec(&g_stLockBlackList); - bbox_print(PRINT_DBG, "add blacklist addr successed, len = %llu.\n", uiLen); - - return RET_OK; -} - -/* - * drop a blaclist item to dump it in core file. - * void *pAddress : the head address of excluded memory - * return RET_OK if success else RET_ERR. - */ -int _BBOX_RmvBlackListAddress(void* pAddress) -{ - unsigned int uiFound = 0; - - if (pAddress == NULL) { - bbox_print(PRINT_ERR, "parameter pAddress is invaild.\n"); - return RET_ERR; - } - - /* use atomic increment to control concurrency. */ - while (BBOX_AtomicIncReturn(&g_stLockBlackList) > 1) { - BBOX_AtomicDec(&g_stLockBlackList); - bbox_print(PRINT_DBG, "remove blacklist addr is running, waiting.\n"); - sleep(1); - } - - /* if blacklist is empty, return error. */ - if (g_iNumBlackList == 0) { - BBOX_AtomicDec(&g_stLockBlackList); - bbox_print(PRINT_ERR, "blacklist addr total is zero, failed.\n"); - return RET_ERR; - } - - /* find the specified address and drop it from blacklist. */ - for (int i = 0; i < g_iNumBlackList; i++) { - if (pAddress == g_stBlackList[i].pBlackStartAddr) { - uiFound = 1; - } - - /* if found, move subsequent items a step forward. */ - if (uiFound == 1) { - /* if it is the last, clear it and stop. */ - if (i == (g_iNumBlackList - 1)) { - int rc = memset_s(&g_stBlackList[i], sizeof(g_stBlackList[0]), 0, sizeof(g_stBlackList[0])); - securec_check_c(rc, "\0", "\0"); - break; - } - g_stBlackList[i].pBlackStartAddr = g_stBlackList[i+1].pBlackStartAddr; - g_stBlackList[i].pBlackEndAddr= g_stBlackList[i+1].pBlackEndAddr; - g_stBlackList[i].uiLength = g_stBlackList[i+1].uiLength; - } - } - - if (uiFound == 0) { - BBOX_AtomicDec(&g_stLockBlackList); - bbox_print(PRINT_ERR, "remove addr not in blacklist, failed.\n"); - return RET_ERR; - } - - g_iNumBlackList--; - BBOX_AtomicDec(&g_stLockBlackList); - bbox_print(PRINT_DBG, "add blacklist addr successed.\n"); - - return RET_OK; -} - -/* - * get status information of process - * in : char *pBuffer - buffer to store result - * unsigned int uiBufLen - buffer size - * return : count of character store into buffer - success - * RET_ERR - failed - */ -int BBOX_GetStatusInfo(char* pBuffer, unsigned int uiBufLen) -{ - int iResult = 0; - int iStatFD = -1; - int iAllSize = 0; - int iReadSize = 0; - - if (NULL == pBuffer) { - bbox_print(PRINT_ERR, "BBOX_GetStatusInfo parameters is invalid.\n"); - return RET_ERR; - } - - /* information title */ - iResult = bbox_snprintf(pBuffer, uiBufLen, "\nSTATUS INFO\n--------------------------------------------\n"); - if (iResult <= 0 || iResult > (int)uiBufLen) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - return RET_ERR; - } - - --iResult; - pBuffer += iResult; - uiBufLen -= iResult; - iAllSize += iResult; - - /* open /proc/self/status */ - BBOX_NOINTR(iStatFD = sys_open(BBOX_SELF_STATUS_PATH, O_RDONLY, 0)); - if (iStatFD < 0) { - bbox_print(PRINT_ERR, "sys_open is failed, errno = %d.\n", errno); - return RET_ERR; - } - - /* read /proc/self/status */ - BBOX_NOINTR(iReadSize = sys_read(iStatFD, pBuffer, uiBufLen)); - if (iReadSize < 0) { - (void)sys_close(iStatFD); - bbox_print(PRINT_ERR, "sys_read is failed, errno = %d.\n", errno); - return RET_ERR; - } - - iAllSize += iReadSize; - (void)sys_close(iStatFD); - - return iAllSize; -} - -/* - * get status information of cpu - * in : char *pBuffer - buffer to store result - * unsigned int uiBufLen - buffer size - * return : count of character store into buffer - success - * RET_ERR - failed - */ -int BBOX_GetCpuInfo(char* pBuffer, unsigned int uiBufLen) -{ - int iResult = 0; - int iStatFD = -1; - int iAllSize = 0; - int iReadSize = 0; - - if (NULL == pBuffer) { - bbox_print(PRINT_ERR, "BBOX_GetCpuInfo parameters is invalid.\n"); - return RET_ERR; - } - - /* information title */ - iResult = bbox_snprintf(pBuffer, uiBufLen, "\nCPU INFO\n--------------------------------------------\n"); - if (iResult <= 0 || iResult > (int)uiBufLen) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - return RET_ERR; - } - - --iResult; - pBuffer += iResult; - uiBufLen -= iResult; - iAllSize += iResult; - - /* open /proc/stat */ - BBOX_NOINTR(iStatFD = sys_open(BBOX_PROC_INTER_PATH, O_RDONLY, 0)); - if (iStatFD < 0) { - bbox_print(PRINT_ERR, "sys_open is failed, errno = %d.\n", errno); - return RET_ERR; - } - - /* read /proc/stat */ - BBOX_NOINTR(iReadSize = sys_read(iStatFD, pBuffer, uiBufLen)); - if (iReadSize < 0) { - (void)sys_close(iStatFD); - bbox_print(PRINT_ERR, "sys_read is failed, errno = %d.\n", errno); - return RET_ERR; - } - - iAllSize += iReadSize; - (void)sys_close(iStatFD); - - return iAllSize; -} - -/* - * get information of system internal storage - * in : char *pBuffer - buffer to store result - * unsigned int uiBufLen - buffer size - * return : count of character store into buffer - success - * RET_ERR - failed - */ -int BBOX_GetMemInfo(char* pBuffer, unsigned int uiBufLen) -{ - int iResult = 0; - int iStatFD = -1; - int iAllSize = 0; - int iReadSize = 0; - - if (NULL == pBuffer) { - bbox_print(PRINT_ERR, "BBOX_GetMemInfo parameters is invalid.\n"); - return RET_ERR; - } - - /* information title */ - iResult = bbox_snprintf(pBuffer, uiBufLen, "\nMEM INFO\n--------------------------------------------\n"); - if (iResult <= 0 || iResult > (int)uiBufLen) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - return RET_ERR; - } - - --iResult; - pBuffer += iResult; - uiBufLen -= iResult; - iAllSize += iResult; - - /* open /proc/meminfo */ - BBOX_NOINTR(iStatFD = sys_open(BBOX_PROC_MEMINFO_PATH, O_RDONLY, 0)); - if (iStatFD < 0) { - bbox_print(PRINT_ERR, "sys_open is failed, iStatFD = %d.\n", iStatFD); - return RET_ERR; - } - - /* read /proc/meminfo */ - BBOX_NOINTR(iReadSize = sys_read(iStatFD, pBuffer, uiBufLen)); - if (iReadSize < 0) { - (void)sys_close(iStatFD); - bbox_print(PRINT_ERR, "sys_read is failed, iReadSize = %d.\n", iReadSize); - return RET_ERR; - } - - iAllSize += iReadSize; - (void)sys_close(iStatFD); - - return iAllSize; -} - -/* - * get information of ps command - * in : char *pBuffer - buffer to write result information - * : unsigned int uiBufLen - size of buffer - * return : success - count of characters written to the buffer - * failed - RET_ERR - */ -int BBOX_GetPsInfo(char* pBuffer, unsigned int uiBufLen) -{ - int iResult = 0; - int iCommandFD = -1; - int iAllSize = 0; - int iReadSize = 0; - - if (NULL == pBuffer) { - bbox_print(PRINT_ERR, "BBOX_GetPsInfo parameters is invalid.\n"); - - return RET_ERR; - } - - /* information title */ - iResult = bbox_snprintf(pBuffer, uiBufLen, "\nPROCESS INFO\n--------------------------------------------\n"); - if (iResult <= 0 || iResult > (int)uiBufLen) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - return RET_ERR; - } - - --iResult; - pBuffer += iResult; /* calculate the start index of next part */ - uiBufLen -= iResult; /* calculate free length */ - iAllSize += iResult; /* counts the number of characters written to buffer */ - - /* run ps commend */ - BBOX_NOINTR(iCommandFD = sys_popen(BBOX_PS_CMD, "r")); - if (iCommandFD < 0) { - bbox_print(PRINT_ERR, "sys_popen is failed, iStatFD = %d.\n", iCommandFD); - return RET_ERR; - } - - /* read result of cpmmand ps */ - BBOX_NOINTR(iReadSize = sys_read(iCommandFD, pBuffer, uiBufLen)); - if (iReadSize < 0) { - (void)sys_pclose(iCommandFD); - - bbox_print(PRINT_ERR, "sys_read is failed, iReadSize = %d.\n", iReadSize); - - return RET_ERR; - } - - iAllSize += iReadSize; - (void)sys_pclose(iCommandFD); - - if (0 != iReadSize) { - pBuffer[iReadSize - 1] = '\0'; - } - - return iAllSize; -} - -/* - * - * get running information of system - * in : char *pBuffer - the buffer to store information - * unsigned int uiBufLen - size of buffer - * return: success - count of characters written to the buffer - * failed - RET_ERR - */ -int _BBOX_GetAddonInfo(char* pBuffer, unsigned int uiBufLen) -{ - int iResult = 0; - unsigned int uiAllStringSz = 0; - unsigned int uiLastLen = uiBufLen; - char* pBufPos = pBuffer; - errno_t rc = EOK; - - rc = memset_s(pBuffer, uiBufLen, 0, uiBufLen); - securec_check_c(rc, "\0", "\0"); - - /* get status information of process */ - iResult = BBOX_GetStatusInfo(pBufPos, uiLastLen); - if (iResult < 0) { - - bbox_print(PRINT_ERR, "BBOX_GetStatusInfo is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - /* get status information of cpu */ - pBufPos += iResult; /* calculate the offset of next written */ - uiLastLen -= iResult; /* calculate free length */ - uiAllStringSz += iResult; /* counts the number of characters written to buffer */ - iResult = BBOX_GetCpuInfo(pBufPos, uiLastLen); - if (iResult < 0) { - - bbox_print(PRINT_ERR, "BBOX_GetCpuInfo is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - /* get information of system internal storage */ - pBufPos += iResult; /* calculate the offset of next written */ - uiLastLen -= iResult; /* calculate free length */ - uiAllStringSz += iResult; /* counts the number of characters written to buffer */ - iResult = BBOX_GetMemInfo(pBufPos, uiLastLen); - if (iResult < 0) { - - bbox_print(PRINT_ERR, "BBOX_GetMemInfo is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - /* get status information of another process */ - pBufPos += iResult; /* calculate the offset of next written */ - uiLastLen -= iResult; /* calculate free length */ - uiAllStringSz += iResult; /* counts the number of characters written to buffer */ - iResult = BBOX_GetPsInfo(pBufPos, uiLastLen); - if (iResult < 0) { - - bbox_print(PRINT_ERR, "BBOX_GetPSInfo is failed, iResult = %d.\n", iResult); - - return RET_ERR; - } - - uiAllStringSz += iResult; /* counts the number of characters written to buffer */ - - return uiAllStringSz; -} - -#ifdef __cplusplus -#if __cplusplus -} -#endif -#endif /* __cplusplus */ -- 2.34.1 From 101001c72f3adf47e4ffa14977e2be52f47b1931 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:19:37 +0800 Subject: [PATCH 23/56] ADD file via upload --- .../cbb/bbox/bbox_elf_dump_base.cpp | 724 ++++++++++++++++++ 1 file changed, 724 insertions(+) create mode 100644 src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp b/src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp new file mode 100644 index 000000000..ff1433df1 --- /dev/null +++ b/src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp @@ -0,0 +1,724 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * bbox_elf_dump_base.cpp + * + * IDENTIFICATION + * src/gausskernel/cbb/bbox/bbox_elf_dump_base.cpp + * + * ------------------------------------------------------------------------- + */ +#include "bbox.h" +#include "bbox_elf_dump_base.h" +#include "bbox_syscall_support.h" +#include "../../src/include/securec.h" +#include "../../src/include/securec_check.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +/* black list of atomic variable */ +BBOX_ATOMIC_STRU g_stLockBlackList = BBOX_ATOMIC_INIT(0); + +/* the length of black list */ +static int g_iNumBlackList = 0; + +/* the lookup position of black list */ +static int g_iPosBlackList = 0; + +/* array store for black list */ +static BBOX_BLACKLIST_STRU g_stBlackList[BBOX_BLACK_LIST_COUNT_MAX]; + + /* +function name: BBOX_DetermineMsb +description: The function should judge the mode that PC uses to store data is Big-endian/Little-endian. +arguments: void +return value: An integer that indicates the mode is Big-endian/Little-endian, + if it is ELFDATA2LSB, the mode is Little-endian, + if it is ELFDATA2MSB, the mode is Big-endian. +note锛歍he way that this function judge the mode that PC uses to store data is through a union variable unProbe, + at first we give its first member variable sShortInt a value BBOX_MSB_LSB_INT of type short, then its second + member variable cSplit[sizeof(short)] equaling to cSplit[2] would have the equal value of the first. Finally we + just need to compare BBOX_LITTER_BITS and BBOX_HIGH_BITS, namely the low byte and high byte of + BBOX_MSB_LSB_INT, with unProbe.cSplit[0] and unProbe.cSplit[1], if they are correspondingly equal, the mode is + Little-endian, else is the Big-endian. +date: 2022/8/2 +contact tel: 18720816902 + */ +// 用于确定系统的字节序的函数 +int BBOX_DetermineMsb(void) +{ + // 定义一个联合体,用于存储一个short整数并将其拆分为单个字节 + union INT_PROBE { + short sShortInt; // 短整数 + char cSplit[sizeof(short)]; // 字符数组(字节数组),用于拆分整数 + } unProbe; + + // 将联合体中的整数值设置为已知值 + unProbe.sShortInt = BBOX_MSB_LSB_INT; + + // 检查short整数的第一个字节是否与MSB的预期值匹配 + // 并且检查第二个字节是否与LSB的预期值匹配 + if ((BBOX_LITTER_BITS == unProbe.cSplit[0]) && (BBOX_HIGH_BITS == unProbe.cSplit[1])) { + // 如果字节序为LSB,则返回LSB的值 + return ELFDATA2LSB; + } else { + // 如果字节序为MSB,则返回MSB的值 + return ELFDATA2MSB; + } +} + +/* + * converts a string to time + * in : char *pSwitch - the string to be converted + * out : struct BBOX_ELF_TIMEVAL *pstElfTimeval - result + * return : RET_OK - success + * RET_ERR - failed + */ +int BBOX_StringToTime(const char* pSwitch, struct BBOX_ELF_TIMEVAL* pstElfTimeval) +{ + int iSwitchTimes = 0; + + if (NULL == pSwitch || NULL == pstElfTimeval) { + bbox_print(PRINT_ERR, + "BBOX_StringToTime parameters is invalid: pSwitch or pstElfTimeval is NULL.\n"); + return RET_ERR; + } + + /* converts a string to num */ + while (*pSwitch && *pSwitch != ' ') { + iSwitchTimes = DECIMALISM_SPAN * iSwitchTimes + (*pSwitch) - '0'; + pSwitch++; + } + + pstElfTimeval->lTvSec = iSwitchTimes / SEC_CHANGE_MIRCO_SEC; + pstElfTimeval->lTvMicroSec = (iSwitchTimes % SEC_CHANGE_MIRCO_SEC) * SEC_CHANGE_MIRCO_SEC; + + return RET_OK; +} + +/* + * read a character from file + * in : struct BBOX_READ_FILE_IO *pstIO - bbox file pointer + * return : the character read in file - success + * RET_ERR - failed + */ +// 从文件中获取一个字符的函数 +int BBOX_GetCharFromFile(struct BBOX_READ_FILE_IO* pstIO) +{ + ssize_t iReadSize = -1; + + // 检查pstIO是否为NULL + if (NULL == pstIO) { + bbox_print(PRINT_ERR, "BBOX_GetCharFromFile参数无效:pstIO为NULL。\n"); + return RET_ERR; + } + + unsigned char* pTempIO = pstIO->pData; + + // 检查缓冲区是否为空 + if (pTempIO == pstIO->pEnd) { + /* 当缓冲区为空时,从文件中读取字符并将其放入缓冲区 */ + BBOX_NOINTR(iReadSize = sys_read(pstIO->iFd, pstIO->szBuff, sizeof(pstIO->szBuff))); + + // 检查读取的大小 + if (iReadSize <= 0) { + if (0 == iReadSize) { + errno = 0; // 清除错误标记 + } + + return RET_ERR; // 读取出错 + } + + pTempIO = &(pstIO->szBuff[0]); + pstIO->pEnd = &(pstIO->szBuff[iReadSize]); + } + + pstIO->pData = pTempIO + 1; + + return *pTempIO; +} + +/* + * converts a string to num + * in : struct BBOX_READ_FILE_IO *pstIO - file to be read + * out : size_t *pAddress - buffer to store result + * return : the result num - success + * RET_ERR - failed + */ +// 字符串转换为整数的函数, 从文件中读取字符并将其转换为整数 +int BBOX_StringSwitchInt(struct BBOX_READ_FILE_IO* pstIO, size_t* pAddress) +{ + int iMappingTextChar = 0; + + // 检查pstIO和pAddress是否为NULL + if (NULL == pstIO || NULL == pAddress) { + bbox_print( + PRINT_ERR, "BBOX_StringSwitchInt参数无效:pstIO或pAddress为NULL。\n"); + return RET_ERR; + } + + *pAddress = 0; // 初始化pAddress为0 + iMappingTextChar = BBOX_GetCharFromFile(pstIO); // 从文件中获取一个字符 + + // 循环直到遇到非数字字符和非小写字母字符 + while ((iMappingTextChar >= '0' && iMappingTextChar <= '9') || (iMappingTextChar >= 'a' && iMappingTextChar <= 'f')) { + + /* 将变量左移,并在末尾添加由字符转换而来的数字 */ + *pAddress = (*pAddress << ONE_HEXA_DECIMAL_BITS) | + (unsigned int)(iMappingTextChar < 'A' ? iMappingTextChar - '0' + : ((unsigned int)iMappingTextChar & 0xF) + ASC2_CHAR_GREATER_NUM); + + iMappingTextChar = BBOX_GetCharFromFile(pstIO); // 读取下一个字符 + } + + return iMappingTextChar; // 返回读取的字符 +} + +/* + * 当读取文件/proc/self/maps时,忽略无用信息并跳过到行尾,在获得所有必要信息之后。 + * 返回:存储在缓冲区中的字符数 - 成功 + * RET_ERR - 失败 + */ +int BBOX_SkipToLineEnd(struct BBOX_READ_FILE_IO* pstReadIO) +{ + int iGetChar = -1; + + // 检查pstReadIO是否为NULL + if (NULL == pstReadIO) { + bbox_print(PRINT_ERR, "BBOX_SkipToLineEnd参数无效:pstReadIO为NULL。\n"); + return RET_ERR; + } + + do { + /* 读取字符直到换行符 */ + iGetChar = BBOX_GetCharFromFile(pstReadIO); + + // 检查读取字符是否失败 + if (RET_ERR == iGetChar) { + bbox_print(PRINT_ERR, "BBOX_SkipToLineEnd失败,iGetChar = %d。\n", iGetChar); + return RET_ERR; + } + } while (iGetChar != '\n'); + + return RET_OK; +} + +/* + * 判断地址范围是否在黑名单中,如果是,则返回覆盖它的黑名单项,否则返回NULL。 + * + * 注意:该函数是一个线程不安全的函数,因为它作为迭代器使用。 + */ +BBOX_BLACKLIST_STRU *_BBOX_FindAddrInBlackList(const void* pStartAddress, const void* pEndAddress) +{ + int i = 0; + int iPerformance = 0; + size_t uiPageSize = sys_sysconf(_SC_PAGESIZE); + + /* 如果游标达到黑名单的末尾,则开始新的遍历。 */ + if (g_iPosBlackList >= g_iNumBlackList) { + g_iPosBlackList = 0; + } + + for (i = g_iPosBlackList; i < g_iNumBlackList; i++) { + iPerformance++; + + /* + * 如果段的结束地址小于此项的开始地址, + * 这意味着不会与剩余的黑名单项相交,因为黑名单项是按递增顺序排列的。 + */ + if (pEndAddress <= g_stBlackList[i].pBlackStartAddr) { + bbox_print(PRINT_DBG, "\n通过%d次查找,POS = %d找到黑名单。\n\n", iPerformance, g_iPosBlackList); + bbox_print(PRINT_DBG, "在段中未找到黑名单。\n"); + return NULL; + } + + if (pStartAddress <= g_stBlackList[i].pBlackStartAddr && + g_stBlackList[i].pBlackStartAddr < pEndAddress) { + g_iPosBlackList = i; + bbox_print(PRINT_DBG, "\n通过%d次查找,POS = %d找到黑名单。\n\n", iPerformance, g_iPosBlackList); + bbox_print(PRINT_DBG, "在段中找到黑名单。\n"); + return &(g_stBlackList[i]); + } + + if (pStartAddress < g_stBlackList[i].pBlackEndAddr && + g_stBlackList[i].pBlackEndAddr <= pEndAddress) { + if (((uintptr_t)pEndAddress - (uintptr_t)(g_stBlackList[i].pBlackEndAddr)) < uiPageSize) { + bbox_print(PRINT_DBG, "在段中找到黑名单,但大小小于4K,不用关心,返回NULL。\n"); + return NULL; + } else { + g_iPosBlackList = i; + bbox_print(PRINT_DBG, "\n通过%d次查找,POS = %d找到黑名单。\n\n", iPerformance, g_iPosBlackList); + bbox_print(PRINT_DBG, "在段中找到黑名单。\n"); + return &(g_stBlackList[i]); + } + } + } + + bbox_print(PRINT_DBG, "\n通过%d次查找,POS = %d未找到黑名单。\n\n", iPerformance, g_iPosBlackList); + bbox_print(PRINT_DBG, "在段中未找到黑名单。\n"); + + /* 此段与黑名单项之间无交叉。 */ + return NULL; +} + +/* + * set a blaclist item to drop it from core file. + * void *pAddress : the head address of excluded memory + * unsigned long long uiLen : memory size + * return RET_OK if success else RET_ERR. + */ +/* 向黑名单列表添加地址 */ +int _BBOX_AddBlackListAddress(void* pAddress, unsigned long long uiLen) +{ + unsigned int uiFound = 0; + + // 如果地址为空或者长度小于最小限制,打印错误信息并返回错误码 + if (pAddress == NULL || uiLen < BBOX_BLACK_LIST_MIN_LEN) { + bbox_print(PRINT_ERR, "parameter uiLen(%llu) is invaild.\n", uiLen); + return RET_ERR; + } + + /* 使用原子增加操作来控制并发 */ + while (BBOX_AtomicIncReturn(&g_stLockBlackList) > 1) { + BBOX_AtomicDec(&g_stLockBlackList); + bbox_print(PRINT_DBG, "add blacklist addr is running, waiting.\n"); + sleep(1); + } + + // 如果黑名单项目数量达到上限,打印错误信息并返回错误码 + if (g_iNumBlackList >= BBOX_BLACK_LIST_COUNT_MAX) { + BBOX_AtomicDec(&g_stLockBlackList); + bbox_print(PRINT_ERR, "blacklist addr total reach max, failed.\n"); + return RET_ERR; + } + + /* + * 假设地址越来越大,从黑名单末尾开始往前移动, + * 找到适合将该地址插入到黑名单的位置 + */ + for (int i = g_iNumBlackList - 1; i >= 0; i--) { + /* 如果试图再次添加相同的地址,报告错误 */ + if (g_stBlackList[i].pBlackStartAddr == pAddress) { + BBOX_AtomicDec(&g_stLockBlackList); + + if (g_stBlackList[i].uiLength == uiLen) { + return RET_OK; + } + + bbox_print(PRINT_ERR, "add addr has in blacklist\n"); + return RET_ERR; + } + + if (g_stBlackList[i].pBlackStartAddr > pAddress) { + g_stBlackList[i+1].pBlackStartAddr = g_stBlackList[i].pBlackStartAddr; + g_stBlackList[i+1].pBlackEndAddr = g_stBlackList[i].pBlackEndAddr; + g_stBlackList[i+1].uiLength = g_stBlackList[i].uiLength; + } else { + g_stBlackList[i+1].pBlackStartAddr = pAddress; + g_stBlackList[i+1].uiLength = uiLen; + g_stBlackList[i+1].pBlackEndAddr= (void *)((char *)pAddress + uiLen); + uiFound = 1; + break; + } + } + + /* 如果没有找到,说明该地址比所有地址都小,将其置于首位 */ + if (uiFound == 0) { + g_stBlackList[0].pBlackStartAddr = pAddress; + g_stBlackList[0].uiLength = uiLen; + g_stBlackList[0].pBlackEndAddr= (void *)((char *)pAddress + uiLen); + } + + ++g_iNumBlackList; + + BBOX_AtomicDec(&g_stLockBlackList); + bbox_print(PRINT_DBG, "add blacklist addr successed, len = %llu.\n", uiLen); + + return RET_OK; +} + +/* + * 将黑名单中的项删除,并将其转储到核心文件中。 + * void *pAddress : 被排除内存的起始地址 + * return RET_OK 成功,否则返回 RET_ERR。 + */ +int _BBOX_RmvBlackListAddress(void* pAddress) +{ + unsigned int uiFound = 0; + + // 如果地址为空,打印错误信息并返回错误码 + if (pAddress == NULL) { + bbox_print(PRINT_ERR, "parameter pAddress is invaild.\n"); + return RET_ERR; + } + + /* 使用原子增加操作来控制并发 */ + while (BBOX_AtomicIncReturn(&g_stLockBlackList) > 1) { + BBOX_AtomicDec(&g_stLockBlackList); + bbox_print(PRINT_DBG, "remove blacklist addr is running, waiting.\n"); + sleep(1); + } + + // 如果黑名单为空,返回错误码 + if (g_iNumBlackList == 0) { + BBOX_AtomicDec(&g_stLockBlackList); + bbox_print(PRINT_ERR, "blacklist addr total is zero, failed.\n"); + return RET_ERR; + } + + // 找到指定的地址并从黑名单中删除 + for (int i = 0; i < g_iNumBlackList; i++) { + if (pAddress == g_stBlackList[i].pBlackStartAddr) { + uiFound = 1; + } + + /* 如果找到,将后续项目向前移动一步 */ + if (uiFound == 1) { + /* 如果是最后一个,清除并停止 */ + if (i == (g_iNumBlackList - 1)) { + int rc = memset_s(&g_stBlackList[i], sizeof(g_stBlackList[0]), 0, sizeof(g_stBlackList[0])); + securec_check_c(rc, "\0", "\0"); + break; + } + g_stBlackList[i].pBlackStartAddr = g_stBlackList[i+1].pBlackStartAddr; + g_stBlackList[i].pBlackEndAddr= g_stBlackList[i+1].pBlackEndAddr; + g_stBlackList[i].uiLength = g_stBlackList[i+1].uiLength; + } + } + + if (uiFound == 0) { + BBOX_AtomicDec(&g_stLockBlackList); + bbox_print(PRINT_ERR, "remove addr not in blacklist, failed.\n"); + return RET_ERR; + } + + g_iNumBlackList--; + BBOX_AtomicDec(&g_stLockBlackList); + bbox_print(PRINT_DBG, "add blacklist addr successed.\n"); + + return RET_OK; +} + +/* + * get status information of process + * in : char *pBuffer - buffer to store result + * unsigned int uiBufLen - buffer size + * return : count of character store into buffer - success + * RET_ERR - failed + */ +```c +// 获取系统状态信息 +int BBOX_GetStatusInfo(char* pBuffer, unsigned int uiBufLen) +{ + int iResult = 0; + int iStatFD = -1; + int iAllSize = 0; + int iReadSize = 0; + + // 检查参数是否有效 + if (NULL == pBuffer) { + bbox_print(PRINT_ERR, "BBOX_GetStatusInfo参数无效。\n"); + return RET_ERR; + } + + /* 信息标题 */ + iResult = bbox_snprintf(pBuffer, uiBufLen, "\n状态信息\n--------------------------------------------\n"); + if (iResult <= 0 || iResult > (int)uiBufLen) { + bbox_print(PRINT_ERR, "bbox_snprintf执行失败,errno = %d。\n", errno); + return RET_ERR; + } + + --iResult; + pBuffer += iResult; + uiBufLen -= iResult; + iAllSize += iResult; + + /* 打开/proc/self/status */ + BBOX_NOINTR(iStatFD = sys_open(BBOX_SELF_STATUS_PATH, O_RDONLY, 0)); + if (iStatFD < 0) { + bbox_print(PRINT_ERR, "sys_open执行失败,errno = %d。\n", errno); + return RET_ERR; + } + + /* 读取/proc/self/status */ + BBOX_NOINTR(iReadSize = sys_read(iStatFD, pBuffer, uiBufLen)); + if (iReadSize < 0) { + (void)sys_close(iStatFD); + bbox_print(PRINT_ERR, "sys_read执行失败,errno = %d。\n", errno); + return RET_ERR; + } + + iAllSize += iReadSize; + (void)sys_close(iStatFD); + + return iAllSize; +} + +/* + * 获取CPU信息 + * 输入:char *pBuffer - 存储结果的缓冲区 + * unsigned int uiBufLen - 缓冲区大小 + * 返回值:存储到缓冲区的字符数 - 成功 + * RET_ERR - 失败 + */ +int BBOX_GetCpuInfo(char* pBuffer, unsigned int uiBufLen) +{ + int iResult = 0; + int iStatFD = -1; + int iAllSize = 0; + int iReadSize = 0; + + // 检查参数是否有效 + if (NULL == pBuffer) { + bbox_print(PRINT_ERR, "BBOX_GetCpuInfo参数无效。\n"); + return RET_ERR; + } + + /* 信息标题 */ + iResult = bbox_snprintf(pBuffer, uiBufLen, "\nCPU信息\n--------------------------------------------\n"); + if (iResult <= 0 || iResult > (int)uiBufLen) { + bbox_print(PRINT_ERR, "bbox_snprintf执行失败,errno = %d。\n", errno); + return RET_ERR; + } + + --iResult; + pBuffer += iResult; + uiBufLen -= iResult; + iAllSize += iResult; + + /* 打开/proc/stat */ + BBOX_NOINTR(iStatFD = sys_open(BBOX_PROC_INTER_PATH, O_RDONLY, 0)); + if (iStatFD < 0) { + bbox_print(PRINT_ERR, "sys_open执行失败,errno = %d。\n", errno); + return RET_ERR; + } + + /* 读取/proc/stat */ + BBOX_NOINTR(iReadSize = sys_read(iStatFD, pBuffer, uiBufLen)); + if (iReadSize < 0) { + (void)sys_close(iStatFD); + bbox_print(PRINT_ERR, "sys_read执行失败,errno = %d。\n", errno); + return RET_ERR; + } + + iAllSize += iReadSize; + (void)sys_close(iStatFD); + + return iAllSize; +} + +/* + * 获取系统内存信息 + * 输入:char *pBuffer - 存储结果的缓冲区 + * unsigned int uiBufLen - 缓冲区大小 + * 返回值:存储到缓冲区的字符数 - 成功 + * RET_ERR - 失败 + */ +int BBOX_GetMemInfo(char* pBuffer, unsigned int uiBufLen) +{ + int iResult = 0; + int iStatFD = -1; + int iAllSize = 0; + int iReadSize = 0; + + // 检查参数是否有效 + if (NULL == pBuffer) { + bbox_print(PRINT_ERR, "BBOX_GetMemInfo参数无效。\n"); + return RET_ERR; + } + + /* 信息标题 */ + iResult = bbox_snprintf(pBuffer, uiBufLen, "\n内存信息\n--------------------------------------------\n"); + if (iResult <= 0 || iResult > (int)uiBufLen) { + bbox_print(PRINT_ERR, "bbox_snprintf执行失败,errno = %d。\n", errno); + return RET_ERR; + } + + --iResult; + pBuffer += iResult; + uiBufLen -= iResult; + iAllSize += iResult; + + /* 打开/proc/meminfo */ + BBOX_NOINTR(iStatFD = sys_open(BBOX_PROC_MEMINFO_PATH, O_RDONLY, 0)); + if (iStatFD < 0) { + bbox_print(PRINT_ERR, "sys_open执行失败,iStatFD = %d。\n", iStatFD); + return RET_ERR; + } + + /* 读取/proc/meminfo */ + BBOX_NOINTR(iReadSize = sys_read(iStatFD, pBuffer, uiBufLen)); + if (iReadSize < 0) { + (void)sys_close(iStatFD); + bbox_print(PRINT_ERR, "sys_read执行失败,iReadSize = %d。\n", iReadSize); + return RET_ERR; + } + + iAllSize += iReadSize; + (void)sys_close(iStatFD); + + return iAllSize; +} + + +/* + * get information of ps command + * in : char *pBuffer - buffer to write result information + * : unsigned int uiBufLen - size of buffer + * return : success - count of characters written to the buffer + * failed - RET_ERR + */ + /* +这段代码是一个用于获取系统运行信息的函数。函数名为BBOX_GetPsInfo, +接受两个参数:一个字符指针pBuffer,用于存储信息的缓冲区;一个无符号整数uiBufLen,表示缓冲区的长度。函数的返回值为成功写入缓冲区的字符数量,如果失败则返回一个错误码。 +函数首先对传入的指针进行了NULL检查,如果pBuffer为NULL,则打印错误消息并返回一个错误码。 +然后,使用bbox_snprintf函数将一些信息标题写入缓冲区,包括换行符和分隔线。如果bbox_snprintf返回值小于等于0,或者大于缓冲区长度,打印一个错误消息,并返回一个错误码。 +接下来,函数通过执行系统命令"ps"来获取进程信息。函数使用sys_popen函数打开一个管道,并将管道的输出连接到iCommandFD文件描述符。如果sys_popen返回的文件描述符小于0,说明打开管道失败,打印一个相关错误消息,并返回一个错误码。 +然后,函数使用sys_read函数从iCommandFD文件描述符中读取数据,并将数据写入缓冲区。如果sys_read返回的字节数小于0,说明读取失败,函数调用sys_pclose函数关闭打开的管道,并打印一个相关错误消息,并返回一个错误码。 +接着,函数将读取到的字节数累加到iAllSize变量中。如果iReadSize不为0,函数将缓冲区中的最后一个字符设置为'\0'。 +最后,函数返回iAllSize变量的值,表示写入缓冲区的总字符数量。 +此外,代码中还有一个函数_BBOX_GetAddonInfo,该函数调用了BBOX_GetPsInfo函数,并对获取的系统运行信息进行了进一步处理。该函数的实现逻辑与BBOX_GetPsInfo类似,但是还调用了其他几个函数来获取不同的系统信息,并将这些信息写入缓冲区。 +最后的#ifdef __cplusplus部分是对C++编译器进行的特殊处理,暂时可以忽略。 + */ +int BBOX_GetPsInfo(char* pBuffer, unsigned int uiBufLen) +{ + int iResult = 0; + int iCommandFD = -1; + int iAllSize = 0; + int iReadSize = 0; + + if (NULL == pBuffer) { + bbox_print(PRINT_ERR, "BBOX_GetPsInfo parameters is invalid.\n"); + + return RET_ERR; + } + + /* information title */ + iResult = bbox_snprintf(pBuffer, uiBufLen, "\nPROCESS INFO\n--------------------------------------------\n"); + if (iResult <= 0 || iResult > (int)uiBufLen) { + bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + return RET_ERR; + } + + --iResult; + pBuffer += iResult; /* calculate the start index of next part */ + uiBufLen -= iResult; /* calculate free length */ + iAllSize += iResult; /* counts the number of characters written to buffer */ + + /* run ps commend */ + BBOX_NOINTR(iCommandFD = sys_popen(BBOX_PS_CMD, "r")); + if (iCommandFD < 0) { + bbox_print(PRINT_ERR, "sys_popen is failed, iStatFD = %d.\n", iCommandFD); + return RET_ERR; + } + + /* read result of cpmmand ps */ + BBOX_NOINTR(iReadSize = sys_read(iCommandFD, pBuffer, uiBufLen)); + if (iReadSize < 0) { + (void)sys_pclose(iCommandFD); + + bbox_print(PRINT_ERR, "sys_read is failed, iReadSize = %d.\n", iReadSize); + + return RET_ERR; + } + + iAllSize += iReadSize; + (void)sys_pclose(iCommandFD); + + if (0 != iReadSize) { + pBuffer[iReadSize - 1] = '\0'; + } + + return iAllSize; +} + +/* + * + * get running information of system + * in : char *pBuffer - the buffer to store information + * unsigned int uiBufLen - size of buffer + * return: success - count of characters written to the buffer + * failed - RET_ERR + */ +int _BBOX_GetAddonInfo(char* pBuffer, unsigned int uiBufLen) +{ + int iResult = 0; + unsigned int uiAllStringSz = 0; + unsigned int uiLastLen = uiBufLen; + char* pBufPos = pBuffer; + errno_t rc = EOK; + + rc = memset_s(pBuffer, uiBufLen, 0, uiBufLen); + securec_check_c(rc, "\0", "\0"); + + /* get status information of process */ + iResult = BBOX_GetStatusInfo(pBufPos, uiLastLen); + if (iResult < 0) { + + bbox_print(PRINT_ERR, "BBOX_GetStatusInfo is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + /* get status information of cpu */ + pBufPos += iResult; /* calculate the offset of next written */ + uiLastLen -= iResult; /* calculate free length */ + uiAllStringSz += iResult; /* counts the number of characters written to buffer */ + iResult = BBOX_GetCpuInfo(pBufPos, uiLastLen); + if (iResult < 0) { + + bbox_print(PRINT_ERR, "BBOX_GetCpuInfo is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + /* get information of system internal storage */ + pBufPos += iResult; /* calculate the offset of next written */ + uiLastLen -= iResult; /* calculate free length */ + uiAllStringSz += iResult; /* counts the number of characters written to buffer */ + iResult = BBOX_GetMemInfo(pBufPos, uiLastLen); + if (iResult < 0) { + + bbox_print(PRINT_ERR, "BBOX_GetMemInfo is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + /* get status information of another process */ + pBufPos += iResult; /* calculate the offset of next written */ + uiLastLen -= iResult; /* calculate free length */ + uiAllStringSz += iResult; /* counts the number of characters written to buffer */ + iResult = BBOX_GetPsInfo(pBufPos, uiLastLen); + if (iResult < 0) { + + bbox_print(PRINT_ERR, "BBOX_GetPSInfo is failed, iResult = %d.\n", iResult); + + return RET_ERR; + } + + uiAllStringSz += iResult; /* counts the number of characters written to buffer */ + + return uiAllStringSz; +} + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ -- 2.34.1 From a7c40a37999e53695728636a002e19c544f46a07 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:58:20 +0800 Subject: [PATCH 24/56] Delete 'src/gausskernel/cbb/bbox/bbox_lib.cpp' --- src/gausskernel/cbb/bbox/bbox_lib.cpp | 580 -------------------------- 1 file changed, 580 deletions(-) delete mode 100644 src/gausskernel/cbb/bbox/bbox_lib.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_lib.cpp b/src/gausskernel/cbb/bbox/bbox_lib.cpp deleted file mode 100644 index f8a1eae7b..000000000 --- a/src/gausskernel/cbb/bbox/bbox_lib.cpp +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * bbox_lib.cpp - * - * IDENTIFICATION - * src/gausskernel/cbb/bbox/bbox_lib.cpp - * - * ------------------------------------------------------------------------- - */ -#include "bbox_syscall_support.h" -#include "bbox_lib.h" -#include "bbox_print.h" -#include "../../src/include/securec.h" -#include "../../src/include/securec_check.h" - -#define PAGE_SIZE 4096 -#define BBOX_MAX_PIDS 32 -#define BBOX_TMP_LEN_32 32 - -#define container_of(ptr, type, member) \ - ({ \ - const typeof(((type*)0)->member)* __mptr = (ptr); \ - (type*)((char*)__mptr - offsetof(type, member)); \ - }) - -#define typeof decltype - -struct PIPE_ID { - s32 iFd; - pid_t pid; -}; - -struct PIPE_IDS { - struct PIPE_ID stPid; - s32 isUsed; -}; - -static struct PIPE_IDS astPipeIds[BBOX_MAX_PIDS]; - -/* -function name: bbox_strncmp -description: To compare two substrings, the pointers pszSrc and pszTarget store their host strings'addresses. -arguments: Two pointers of type const char*, pointing to two strings needed to be compared. - An integer indicates the number of characters at the former of two strings that - will be compared. -return value: Type s32, an interger. - If it's zero, then the former substrings of string pszSrc and pszTarget are same, - else it indicates the difference between the first two characters that these two - strings can't match. -note锛歍he two pointers shouldn't be null. The last argument shouldn't less than zero. -date: 2022/8/2 -contact tel: 18720816902 -*/ -s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count) -{ - signed char cRes = 0; - - while (count) { - if ((cRes = *pszSrc - *pszTarget++) != 0 || !*pszSrc++) { - break; - } - count--; - } - - return cRes; -} - -/* -function name: bbox_strcmp -description: compare two strings, the pointer pszSrc and pszTarget store their addresses. -arguments: Two pointers of type const char*, pointing to two strings needed to be compared. - An integer indicates the number of characters at the former of two strings that - will be compared. -return value: Type s32, an interger. - If it's zero, then the former substrings of string pszSrc and pszTarget are same, - else if it's 1, then it indicates between first two characters that these two - strings can't match, the character of first string that pszSrc points is greater, - else if it's -1, the character of second string that pszTarget points is greater. -note锛歍he two pointers shouldn't be null. The last argument shouldn't less than zero. -date: 2022/8/2 -contact tel:same -*/ -s32 bbox_strcmp(const char* pszSrc, const char* pszTarget) -{ - unsigned char c1, c2; - - while (1) { - c1 = *pszSrc++; - c2 = *pszTarget++; - - if (c1 != c2) { - return c1 < c2 ? -1 : 1; - } - - if (c1 == 0) { - break; - } - } - return 0; -} - -/* -function name: bbox_strlen -description: Calculate the length of string. -arguments: An pointer that indicates the address of a string. -return value: Type s32, an integer indicating the length of string. -note: the length of string=(address of the last character not '\0'-address of the first character)/sizeof(char), and sizeof(char) - equals to 1, so the length of string=(address of the last character not '\0'-address of the first character). -date: 2022/8/2 -contact tel:same -*/ -s32 bbox_strlen(const char* pszString) -{ - const char* pszTemp = NULL; - - for (pszTemp = pszString; *pszTemp != '\0'; ++pszTemp) { - /* nothing */; - continue; - } - - return pszTemp - pszString; -} - -/* -function name: bbox_strnlen -description: Calculate the length of string, but having some restrictive conditions. -arguments: An pointer that indicates the address of a string. - And an integer that indicates the maxlenth. -return value: Type s32, an integer indicating the length of string. -note: If the length of string exceed the argument count, then return the length of string, - else return the argument count. -date: 2022/8/2 -contact tel:same -*/ -s32 bbox_strnlen(const char* pszString, s32 count) -{ - const char* pszTemp = NULL; - - for (pszTemp = pszString; count-- && *pszTemp != '\0'; ++pszTemp) { - /* nothing */; - } - - return pszTemp - pszString; -} - -/* -function name: bbox_atoi -description: Convert a string that includes continuous digital characters to an integer, - if the first character of the string is '-', then we will return a negative result. -arguments: An pointer that indicates the address of a string. -return value: Type s32, an integer indicating the result of string converted. -note: I think the function isn't perfect, though it's not a core function. For example, what about - the condition that the first character of the string is '+'? -date: 2022/8/2 -contact tel:same -*/ -s32 bbox_atoi(const char* pszString) -{ - s32 n = 0; - s32 iNeg = 0; - - if (*pszString == '-') { - iNeg = 1; - } - - if (iNeg) { - pszString++; - } - - while (*pszString >= '0' && *pszString <= '9') { - n = 10 * n + (*pszString++ - '0'); - } - - return iNeg ? -n : n; -} -/* -function name: bbox_memcmp -description: Compare former count bytes in ASCII of data stored in two areas that pointers cs and ct direct. -arguments: Two pointers to areas of memory, and an integer indicating the max counts compared. -return value: Type s32, an integer. - If the value returned is 0, then the data stored in two areas destined are same, - else if is 1, then between two first data in ASCII of byte different, cs's is greater, - else if is -1, then ct's is greater. -note: The two pointers should not be null, it's dangerous. -date: 2022/8/2 -contact tel: same -*/ -s32 bbox_memcmp(const void* cs, const void* ct, s32 count) -{ - const unsigned char *su1 = NULL; - const unsigned char *su2 = NULL; - - for (su1 = (const unsigned char*)cs, su2 = (const unsigned char*)ct; count > 0; ++su1, ++su2, count--) { - if (*su1 != *su2) { - return *su1 < *su2 ? -1 : +1; - } - } - - return 0; -} - -/* -function name: bbox_strstr -description: Judge if the string s2 directs is substring of string s1 directs. -arguments: Two pointers of type const char*, pointing to two strings. -return value: Type char*, a pointer. Actually it's a address, if s2 directs a - null string, then return the address of the first character of s1, - if the string s2 directs isn't substring of string s1 directs, return - null, if the string s2 directs is substring of string s1 directs, then return - the address of first character matched. -note: The two pointers should not be null, it's dangerous. -date: 2022/8/2 -contact tel: same -*/ -char* bbox_strstr(const char* s1, const char* s2) -{ - int l1, l2; - - l2 = bbox_strlen(s2); - if (!l2) { - return (char*)s1; - } - - l1 = bbox_strlen(s1); - while (l1 >= l2) { - l1--; - if (!bbox_memcmp(s1, s2, l2)) { - return (char*)s1; - } - s1++; - } - return NULL; -} - -/* -function name: bbox_mkdir -description: We distinguish parent directory and child directory through character '/', - normally through a for loop, we can make sure all directories above the directory - we want to creat exist, finally we will creat the flag directory after its parent. -arguments: A pointers of type const char*, pointing to one strings, which indicates the filename and its full path. -return value: An integer of type s32, if it's RET_ERR, then we fail to make a directory, else if it's RET_OK then we succeed. -note: Take care the last non-null character of the string needed to be '/', and once if flag directory's - ancestors aren't exist, the function return RET_ERR. -date: 2022/8/2 -contact tel: same -*/ -s32 bbox_mkdir(const char* pszDir) -{ - char szDirName[BBOX_TMP_LEN_32 * 16]; - char* p = NULL; - s32 len; - - if (bbox_snprintf(szDirName, sizeof(szDirName), "%s", pszDir) <= 0) { - return RET_ERR; - } - - len = bbox_strnlen(szDirName, sizeof(szDirName)); - if (szDirName[len - 1] == '/') { - if (len == 1) { - return RET_OK; - } - - szDirName[len - 1] = 0; - } - - for (p = szDirName + 1; *p; p++) { - if (*p != '/') { - continue; - } - - *p = 0; - if (sys_mkdir(szDirName, S_IRWXU) < 0) { - if (errno != EEXIST) { - return RET_ERR; - } - } - - *p = '/'; - } - - if (sys_mkdir(szDirName, S_IRWXU) < 0) { - if (errno != EEXIST) { - return RET_ERR; - } - } - - return RET_OK; -} - -/* -function name: bbox_GetFreePid -description: Through a for loop, we search a free pipe in a structure array, to an array element if its - member variable isUsed's value is 0, we return the array element's another member variable - stPid's address. -arguments: void -return value: An pointer of type struct PIPE_ID* or NULL. -note: none -date: 2022/8/2 -contact tel: same -*/ -struct PIPE_ID* bbox_GetFreePid(void) -{ - u32 i; - - for (i = 0; i < BBOX_MAX_PIDS; i++) { - if (astPipeIds[i].isUsed == 0) { - astPipeIds[i].isUsed = 1; - return &(astPipeIds[i].stPid); - } - } - - return NULL; -} - -/* -function name: bbox_PutPid -description: Release the occupied pipe. -arguments: A pointer of type struct PIPE_ID*. -return value: void -note: If the argument pointer is null, then there is no need to free the storage, the function ends. -date: 2022/8/2 -contact tel: same -*/ -void bbox_PutPid(struct PIPE_ID* pstPid) -{ - struct PIPE_IDS* pstPids = NULL; - if (pstPid == NULL) { - return; - } - - pstPids = container_of(pstPid, struct PIPE_IDS, stPid); - - errno_t rc = memset_s(pstPids, sizeof(struct PIPE_IDS), 0, sizeof(struct PIPE_IDS)); - securec_check_c(rc, "\0", "\0"); -} - -/* -function name: bbox_FindPid -description: In all occupied pipes, the function search the flag pipe through compare all structure - array elements's member variable stPid's member variable iFd with the function - argument iFd, if they are equal, then return the addres of this array elements. -arguments: An integer that indicates a file's file handle. -return value: A pointer of type struct PIPE_ID* or NULL. -note: none -date: 2022/8/2 -contact tel: same -*/ -struct PIPE_ID* bbox_FindPid(int iFd) -{ - u32 i; - - for (i = 0; i < BBOX_MAX_PIDS; i++) { - if (astPipeIds[i].isUsed == 0) { - continue; - } - - if (astPipeIds[i].stPid.iFd == iFd) { - return &(astPipeIds[i].stPid); - } - } - - return NULL; -} - -/* -function name: sys_popen -description: The function gets a free pipe by function bbox_GetFreePid, if normally, then creat a pipe - through sys_pipe, andcreat a child process through function sys_fork, execute a shell command - to run a process. -arguments: One pointer to a string that represents command line, another pointer of type const char* - indicates that the file file handle directs is used in the this mode. -return value: A pointer of type struct PIPE_ID* or NULL. -note: The string that indicates pszMode should only be "r" or "w", -date: 2022/8/2 -contact tel: same -*/ -s32 sys_popen(char* pszCmd, const char* pszMode) -{ - struct PIPE_ID* volatile stCurPid = NULL; - s32 iFd; - s32 saIpedes[2] = {0}; - pid_t pid; - - if (NULL == pszCmd || NULL == pszMode) { - errno = EINVAL; - return -1; - } - - /* determine whether the read-write mode is correct */ - if ((*pszMode != 'r' && *pszMode != 'w') || pszMode[1] != '\0') { - errno = EINVAL; - return -1; - } - - /* get free pipe id */ - stCurPid = bbox_GetFreePid(); - if (NULL == stCurPid) { - return -1; - } - - /* create pipe */ - if (sys_pipe(saIpedes) < 0) { - return -1; - } - - /* create child prosess */ - pid = sys_fork(); - if (pid < 0) { - /* close fd if error */ - sys_close(saIpedes[0]); - sys_close(saIpedes[1]); - bbox_PutPid(stCurPid); - return -1; - } else if (pid == 0) { /* Child. */ - /* child prosess */ - s32 i = 0; - char* pArgv[4]; - pArgv[0] = "sh"; - pArgv[1] = "-c"; - pArgv[2] = pszCmd; - pArgv[3] = 0; - - /* Restore signal function */ - sys_signal(SIGQUIT, SIG_DFL); - sys_signal(SIGTSTP, SIG_IGN); - sys_signal(SIGTERM, SIG_DFL); - sys_signal(SIGINT, SIG_DFL); - - for (i = STDERR_FILENO + 1; i < 1024; i++) { - /* do not close pipe. */ - if (i == saIpedes[0] || i == saIpedes[1]) { - continue; - } - - sys_close((int)i); - } - - if (*pszMode == 'r') { - int tpipedes1 = saIpedes[1]; - - sys_close(saIpedes[0]); - /* - * We must NOT modify saIpedes, due to the - * semantics of vfork. - */ - if (tpipedes1 != STDOUT_FILENO) { - sys_dup2(tpipedes1, STDOUT_FILENO); - sys_close(tpipedes1); - tpipedes1 = STDOUT_FILENO; - } - } else { - sys_close(saIpedes[1]); - if (saIpedes[0] != STDIN_FILENO) { - sys_dup2(saIpedes[0], STDIN_FILENO); - sys_close(saIpedes[0]); - } - } - - sys_execve("/bin/sh", pArgv, environ); - bbox_print(PRINT_ERR, "exec '%s' failed, errno = %d, pid = %d\n", pszCmd, errno, pid); - sys_exit(127); - /* NOTREACHED */ - } - - /* Parent; assume fdopen can't fail. */ - if (*pszMode == 'r') { - iFd = saIpedes[0]; - sys_close(saIpedes[1]); - } else { - iFd = saIpedes[1]; - sys_close(saIpedes[0]); - } - - /* Link into list of file descriptors. */ - stCurPid->iFd = iFd; - stCurPid->pid = pid; - return iFd; -} - -/* -function name: sys_pclose -description: The function has an contrary action to function sys_popen, it close the pipe - that sys_popen open. -arguments: iFd, an integer that indicates a file handle. -return value: An integer that indicates the final status of the process working before. -note: none -date: 2022/8/2 -contact tel: same -*/ -int sys_pclose(s32 iFd) -{ - struct PIPE_ID* pstCur = NULL; - s32 iStat = -1; - pid_t pid; - - pstCur = bbox_FindPid(iFd); - if (pstCur == NULL) { - return -1; - } - - sys_close(iFd); - - do { - pid = sys_waitpid(pstCur->pid, &iStat, 0); - } while (pid == -1 && errno == EINTR); - - bbox_PutPid(pstCur); - return (pid == -1 ? -1 : iStat); -} - -/* -function name: bbox_listdir -description: The function list all files below this path in directory. -arguments: The first argument is a pointer to a string representing a file path, all files below - this path will be listed in directory. The second argument is a pointer to a callback - function. The last is a pointer of type void*, it indicates a command line. -return value: An integer that indicates the result of function, if normal, it's RET_OK, else - it's RET_ERR. -note: The path that the first argument represents should be absolute path, take care. -date: 2022/8/2 -contact tel: same -*/ -s32 bbox_listdir(const char* pstPath, BBOX_LIST_DIR_CALLBACK callback, void* pArgs) -{ - struct linux_dirent* pstEntry = NULL; - s32 iDir; - char szBuff[PAGE_SIZE]; - ssize_t nBytes; - s32 iRet = RET_OK; - - if (callback == NULL || pstPath == NULL) { - return RET_ERR; - } - - iDir = sys_open(pstPath, O_RDONLY | O_DIRECTORY, 0); - if (iDir < 0) { - bbox_print(PRINT_ERR, "open directory failed, errno = %d\n", errno); - return RET_ERR; - } - - /* get the file in directory */ - do { - nBytes = sys_getdents(iDir, (struct linux_dirent*)szBuff, sizeof(szBuff)); - if (nBytes < 0) { - bbox_print(PRINT_ERR, "get directory ents failed, errno = %d\n", errno); - sys_close(iDir); - return RET_ERR; - } else if (nBytes == 0) { - /* break when there is no file not read */ - break; - } - - for (pstEntry = (struct linux_dirent*)szBuff; - (pstEntry < (struct linux_dirent*)&szBuff[nBytes]) && iRet == RET_OK; - pstEntry = (struct linux_dirent*)((char*)pstEntry + pstEntry->d_reclen)) { - if (pstEntry->d_ino == 0) { - continue; - } - - iRet = callback(pstPath, pstEntry->d_name, pArgs); - } - } while (nBytes > 0 && iRet == RET_OK); - - sys_close(iDir); - - return iRet; -} -- 2.34.1 From 6222d3d01852ad7aa2097e83002ce1a9062f4e1e Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:58:39 +0800 Subject: [PATCH 25/56] ADD file via upload --- src/gausskernel/cbb/bbox/bbox_lib.cpp | 619 ++++++++++++++++++++++++++ 1 file changed, 619 insertions(+) create mode 100644 src/gausskernel/cbb/bbox/bbox_lib.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_lib.cpp b/src/gausskernel/cbb/bbox/bbox_lib.cpp new file mode 100644 index 000000000..908105d23 --- /dev/null +++ b/src/gausskernel/cbb/bbox/bbox_lib.cpp @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * bbox_lib.cpp + * + * IDENTIFICATION + * src/gausskernel/cbb/bbox/bbox_lib.cpp + * + * ------------------------------------------------------------------------- + */ +#include "bbox_syscall_support.h" +#include "bbox_lib.h" +#include "bbox_print.h" +#include "../../src/include/securec.h" +#include "../../src/include/securec_check.h" + +#define PAGE_SIZE 4096 +#define BBOX_MAX_PIDS 32 +#define BBOX_TMP_LEN_32 32 + +#define container_of(ptr, type, member) \ + ({ \ + const typeof(((type*)0)->member)* __mptr = (ptr); \ + (type*)((char*)__mptr - offsetof(type, member)); \ + }) + +#define typeof decltype + +struct PIPE_ID { + s32 iFd; + pid_t pid; +}; + +struct PIPE_IDS { + struct PIPE_ID stPid; + s32 isUsed; +}; + +static struct PIPE_IDS astPipeIds[BBOX_MAX_PIDS]; + +/* +function name: bbox_strncmp +description: To compare two substrings, the pointers pszSrc and pszTarget store their host strings'addresses. +arguments: Two pointers of type const char*, pointing to two strings needed to be compared. + An integer indicates the number of characters at the former of two strings that + will be compared. +return value: Type s32, an interger. + If it's zero, then the former substrings of string pszSrc and pszTarget are same, + else it indicates the difference between the first two characters that these two + strings can't match. +note锛歍he two pointers shouldn't be null. The last argument shouldn't less than zero. +date: 2022/8/2 +contact tel: 18720816902 +*/ +/* +这段代码是一个用于比较字符串的函数bbox_strncmp。它接受三个参数:常量字符指针pszSrc,用于表示源字符串;常量字符指针pszTarget,用于表示目标字符串;整数count,表示要比较的字符数。 +首先,定义一个有符号字符变量cRes并初始化为0。 +然后,使用while循环进行字符串比较,条件为count大于0。每次循环执行以下操作: +首先,将源字符串指针的值与目标字符串指针的值相减,并将结果赋给cRes。如果结果不等于0,或者源字符串指针的值为0(即字符串结束),则跳出循环。 +然后,将目标字符串指针递增1,源字符串指针也递增1。 +最后,将count减1。 +循环结束后,返回变量cRes,它表示最后一次比较的结果。如果cRes为0,则表示两个字符串相等;如果cRes小于0,则表示源字符串小于目标字符串;如果cRes大于0,则表示源字符串大于目标字符串。 +*/ +s32 bbox_strncmp(const char* pszSrc, const char* pszTarget, s32 count) +{ + signed char cRes = 0; + + while (count) { + if ((cRes = *pszSrc - *pszTarget++) != 0 || !*pszSrc++) { + break; + } + count--; + } + + return cRes; +} + +/* +function name: bbox_strcmp +description: compare two strings, the pointer pszSrc and pszTarget store their addresses. +arguments: Two pointers of type const char*, pointing to two strings needed to be compared. + An integer indicates the number of characters at the former of two strings that + will be compared. +return value: Type s32, an interger. + If it's zero, then the former substrings of string pszSrc and pszTarget are same, + else if it's 1, then it indicates between first two characters that these two + strings can't match, the character of first string that pszSrc points is greater, + else if it's -1, the character of second string that pszTarget points is greater. +note锛歍he two pointers shouldn't be null. The last argument shouldn't less than zero. +date: 2022/8/2 +contact tel:same +*/ +s32 bbox_strcmp(const char* pszSrc, const char* pszTarget) +{ + unsigned char c1, c2; + + while (1) { + c1 = *pszSrc++; + c2 = *pszTarget++; + + if (c1 != c2) { + return c1 < c2 ? -1 : 1; + } + + if (c1 == 0) { + break; + } + } + return 0; +} + +/* +function name: bbox_strlen +description: Calculate the length of string. +arguments: An pointer that indicates the address of a string. +return value: Type s32, an integer indicating the length of string. +note: the length of string=(address of the last character not '\0'-address of the first character)/sizeof(char), and sizeof(char) + equals to 1, so the length of string=(address of the last character not '\0'-address of the first character). +date: 2022/8/2 +contact tel:same +*/ +s32 bbox_strlen(const char* pszString) +{ + const char* pszTemp = NULL; + + for (pszTemp = pszString; *pszTemp != '\0'; ++pszTemp) { + /* nothing */; + continue; + } + + return pszTemp - pszString; +} + +/* +function name: bbox_strnlen +description: Calculate the length of string, but having some restrictive conditions. +arguments: An pointer that indicates the address of a string. + And an integer that indicates the maxlenth. +return value: Type s32, an integer indicating the length of string. +note: If the length of string exceed the argument count, then return the length of string, + else return the argument count. +date: 2022/8/2 +contact tel:same +*/ +s32 bbox_strnlen(const char* pszString, s32 count) +{ + const char* pszTemp = NULL; + + for (pszTemp = pszString; count-- && *pszTemp != '\0'; ++pszTemp) { + /* nothing */; + } + + return pszTemp - pszString; +} + +/* +function name: bbox_atoi +description: Convert a string that includes continuous digital characters to an integer, + if the first character of the string is '-', then we will return a negative result. +arguments: An pointer that indicates the address of a string. +return value: Type s32, an integer indicating the result of string converted. +note: I think the function isn't perfect, though it's not a core function. For example, what about + the condition that the first character of the string is '+'? +date: 2022/8/2 +contact tel:same +*/ +/* +这段代码是一个将字符串转换为整数的函数bbox_atoi。它接受一个常量字符指针pszString,表示要转换的字符串,并返回一个整数值。 +首先,定义两个整数变量n和iNeg,并初始化为0。其中,n用于存储转换后的整数值,iNeg用于表示是否为负数。 +然后,判断字符串的第一个字符是否为'-'。如果是,则将iNeg设置为1,表示转换结果为负数。 +如果iNeg为1,将字符串指针向后移动一位,跳过负号。 +接下来,使用while循环,判断当前字符是否为数字字符,即是否在字符范围'0'到'9'之间。 +在循环内部,首先将n乘以10,然后将当前字符减去'0',并累加到n中。 +循环结束后,返回n的值。如果iNeg为1,则表示结果为负数,返回负数的n;如果iNeg为0,则表示结果为正数,返回正数的n。 +*/ +s32 bbox_atoi(const char* pszString) +{ + s32 n = 0; + s32 iNeg = 0; + + if (*pszString == '-') { + iNeg = 1; + } + + if (iNeg) { + pszString++; + } + + while (*pszString >= '0' && *pszString <= '9') { + n = 10 * n + (*pszString++ - '0'); + } + + return iNeg ? -n : n; +} +/* +function name: bbox_memcmp +description: Compare former count bytes in ASCII of data stored in two areas that pointers cs and ct direct. +arguments: Two pointers to areas of memory, and an integer indicating the max counts compared. +return value: Type s32, an integer. + If the value returned is 0, then the data stored in two areas destined are same, + else if is 1, then between two first data in ASCII of byte different, cs's is greater, + else if is -1, then ct's is greater. +note: The two pointers should not be null, it's dangerous. +date: 2022/8/2 +contact tel: same +*/ +s32 bbox_memcmp(const void* cs, const void* ct, s32 count) +{ + const unsigned char *su1 = NULL; + const unsigned char *su2 = NULL; + + for (su1 = (const unsigned char*)cs, su2 = (const unsigned char*)ct; count > 0; ++su1, ++su2, count--) { + if (*su1 != *su2) { + return *su1 < *su2 ? -1 : +1; + } + } + + return 0; +} + +/* +function name: bbox_strstr +description: Judge if the string s2 directs is substring of string s1 directs. +arguments: Two pointers of type const char*, pointing to two strings. +return value: Type char*, a pointer. Actually it's a address, if s2 directs a + null string, then return the address of the first character of s1, + if the string s2 directs isn't substring of string s1 directs, return + null, if the string s2 directs is substring of string s1 directs, then return + the address of first character matched. +note: The two pointers should not be null, it's dangerous. +date: 2022/8/2 +contact tel: same +*/ +char* bbox_strstr(const char* s1, const char* s2) +{ + int l1, l2; + + l2 = bbox_strlen(s2); + if (!l2) { + return (char*)s1; + } + + l1 = bbox_strlen(s1); + while (l1 >= l2) { + l1--; + if (!bbox_memcmp(s1, s2, l2)) { + return (char*)s1; + } + s1++; + } + return NULL; +} + +/* +function name: bbox_mkdir +description: We distinguish parent directory and child directory through character '/', + normally through a for loop, we can make sure all directories above the directory + we want to creat exist, finally we will creat the flag directory after its parent. +arguments: A pointers of type const char*, pointing to one strings, which indicates the filename and its full path. +return value: An integer of type s32, if it's RET_ERR, then we fail to make a directory, else if it's RET_OK then we succeed. +note: Take care the last non-null character of the string needed to be '/', and once if flag directory's + ancestors aren't exist, the function return RET_ERR. +date: 2022/8/2 +contact tel: same +*/ +s32 bbox_mkdir(const char* pszDir) +{ + char szDirName[BBOX_TMP_LEN_32 * 16]; + char* p = NULL; + s32 len; + + if (bbox_snprintf(szDirName, sizeof(szDirName), "%s", pszDir) <= 0) { + return RET_ERR; + } + + len = bbox_strnlen(szDirName, sizeof(szDirName)); + if (szDirName[len - 1] == '/') { + if (len == 1) { + return RET_OK; + } + + szDirName[len - 1] = 0; + } + + for (p = szDirName + 1; *p; p++) { + if (*p != '/') { + continue; + } + + *p = 0; + if (sys_mkdir(szDirName, S_IRWXU) < 0) { + if (errno != EEXIST) { + return RET_ERR; + } + } + + *p = '/'; + } + + if (sys_mkdir(szDirName, S_IRWXU) < 0) { + if (errno != EEXIST) { + return RET_ERR; + } + } + + return RET_OK; +} + +/* +function name: bbox_GetFreePid +description: Through a for loop, we search a free pipe in a structure array, to an array element if its + member variable isUsed's value is 0, we return the array element's another member variable + stPid's address. +arguments: void +return value: An pointer of type struct PIPE_ID* or NULL. +note: none +date: 2022/8/2 +contact tel: same +*/ +struct PIPE_ID* bbox_GetFreePid(void) +{ + u32 i; + + for (i = 0; i < BBOX_MAX_PIDS; i++) { + if (astPipeIds[i].isUsed == 0) { + astPipeIds[i].isUsed = 1; + return &(astPipeIds[i].stPid); + } + } + + return NULL; +} + +/* +function name: bbox_PutPid +description: Release the occupied pipe. +arguments: A pointer of type struct PIPE_ID*. +return value: void +note: If the argument pointer is null, then there is no need to free the storage, the function ends. +date: 2022/8/2 +contact tel: same +*/ +void bbox_PutPid(struct PIPE_ID* pstPid) +{ + struct PIPE_IDS* pstPids = NULL; + if (pstPid == NULL) { + return; + } + + pstPids = container_of(pstPid, struct PIPE_IDS, stPid); + + errno_t rc = memset_s(pstPids, sizeof(struct PIPE_IDS), 0, sizeof(struct PIPE_IDS)); + securec_check_c(rc, "\0", "\0"); +} + +/* +function name: bbox_FindPid +description: In all occupied pipes, the function search the flag pipe through compare all structure + array elements's member variable stPid's member variable iFd with the function + argument iFd, if they are equal, then return the addres of this array elements. +arguments: An integer that indicates a file's file handle. +return value: A pointer of type struct PIPE_ID* or NULL. +note: none +date: 2022/8/2 +contact tel: same +*/ +struct PIPE_ID* bbox_FindPid(int iFd) +{ + u32 i; + + for (i = 0; i < BBOX_MAX_PIDS; i++) { + if (astPipeIds[i].isUsed == 0) { + continue; + } + + if (astPipeIds[i].stPid.iFd == iFd) { + return &(astPipeIds[i].stPid); + } + } + + return NULL; +} + +/* +function name: sys_popen +description: The function gets a free pipe by function bbox_GetFreePid, if normally, then creat a pipe + through sys_pipe, andcreat a child process through function sys_fork, execute a shell command + to run a process. +arguments: One pointer to a string that represents command line, another pointer of type const char* + indicates that the file file handle directs is used in the this mode. +return value: A pointer of type struct PIPE_ID* or NULL. +note: The string that indicates pszMode should only be "r" or "w", +date: 2022/8/2 +contact tel: same +*/ +/* +函数sys_popen的作用是打开一个管道,用于执行指定的命令。 +它接受两个参数:一个字符指针pszCmd,用于存储要执行的命令;一个常量字符指针pszMode,表示管道的读写模式。函数返回一个整数值,表示管道文件描述符。 +首先,函数检查pszCmd和pszMode指针是否为空,如果为空,则设置errno为EINVAL,并返回-1。 +然后,函数检查读写模式是否正确,即pszMode只能为'r'或'w',并且只能有一个字符。如果模式不正确,则设置errno为EINVAL,并返回-1。 +接下来,函数使用bbox_GetFreePid函数获取一个空闲的管道ID。如果获取失败,则返回-1。 +然后,函数使用sys_pipe函数创建一个管道,如果创建失败,则返回-1。 +接着,函数使用sys_fork函数创建子进程。如果创建子进程失败,则关闭管道的文件描述符,释放管道ID,并返回-1。如果创建成功,则在子进程中执行以下操作: + +- 获取待执行的命令和参数数组pArgv。 +- 恢复信号函数。 +- 关闭其他文件描述符,除了管道的读写文件描述符。 +- 根据读写模式设置标准输入输出文件描述符。 +- 使用sys_execve函数执行命令。如果执行失败,则打印错误信息,并使用sys_exit函数退出进程。 +最后,如果当前进程是父进程,则根据读写模式选择要返回的文件描述符,并将管道ID和子进程ID保存起来。 +函数sys_pclose的作用是关闭由sys_popen打开的管道。它接受一个整数值iFd,表示要关闭的文件描述符。函数返回一个整数值,表示进程的最终状态。 +首先,函数使用bbox_FindPid函数根据文件描述符查找对应的管道ID。如果查找失败,则返回-1。 +然后,函数使用sys_close函数关闭文件描述符。 +接着,函数使用sys_waitpid函数等待子进程的退出,并获取进程的状态。如果等待失败,则继续等待,直到成功或出现其他错误。 +最后,函数释放管道ID,并根据等待的结果返回相应的值。 +*/ +s32 sys_popen(char* pszCmd, const char* pszMode) +{ + struct PIPE_ID* volatile stCurPid = NULL; + s32 iFd; + s32 saIpedes[2] = {0}; + pid_t pid; + + if (NULL == pszCmd || NULL == pszMode) { + errno = EINVAL; + return -1; + } + + /* determine whether the read-write mode is correct */ + if ((*pszMode != 'r' && *pszMode != 'w') || pszMode[1] != '\0') { + errno = EINVAL; + return -1; + } + + /* get free pipe id */ + stCurPid = bbox_GetFreePid(); + if (NULL == stCurPid) { + return -1; + } + + /* create pipe */ + if (sys_pipe(saIpedes) < 0) { + return -1; + } + + /* create child prosess */ + pid = sys_fork(); + if (pid < 0) { + /* close fd if error */ + sys_close(saIpedes[0]); + sys_close(saIpedes[1]); + bbox_PutPid(stCurPid); + return -1; + } else if (pid == 0) { /* Child. */ + /* child prosess */ + s32 i = 0; + char* pArgv[4]; + pArgv[0] = "sh"; + pArgv[1] = "-c"; + pArgv[2] = pszCmd; + pArgv[3] = 0; + + /* Restore signal function */ + sys_signal(SIGQUIT, SIG_DFL); + sys_signal(SIGTSTP, SIG_IGN); + sys_signal(SIGTERM, SIG_DFL); + sys_signal(SIGINT, SIG_DFL); + + for (i = STDERR_FILENO + 1; i < 1024; i++) { + /* do not close pipe. */ + if (i == saIpedes[0] || i == saIpedes[1]) { + continue; + } + + sys_close((int)i); + } + + if (*pszMode == 'r') { + int tpipedes1 = saIpedes[1]; + + sys_close(saIpedes[0]); + /* + * We must NOT modify saIpedes, due to the + * semantics of vfork. + */ + if (tpipedes1 != STDOUT_FILENO) { + sys_dup2(tpipedes1, STDOUT_FILENO); + sys_close(tpipedes1); + tpipedes1 = STDOUT_FILENO; + } + } else { + sys_close(saIpedes[1]); + if (saIpedes[0] != STDIN_FILENO) { + sys_dup2(saIpedes[0], STDIN_FILENO); + sys_close(saIpedes[0]); + } + } + + sys_execve("/bin/sh", pArgv, environ); + bbox_print(PRINT_ERR, "exec '%s' failed, errno = %d, pid = %d\n", pszCmd, errno, pid); + sys_exit(127); + /* NOTREACHED */ + } + + /* Parent; assume fdopen can't fail. */ + if (*pszMode == 'r') { + iFd = saIpedes[0]; + sys_close(saIpedes[1]); + } else { + iFd = saIpedes[1]; + sys_close(saIpedes[0]); + } + + /* Link into list of file descriptors. */ + stCurPid->iFd = iFd; + stCurPid->pid = pid; + return iFd; +} + +/* +function name: sys_pclose +description: The function has an contrary action to function sys_popen, it close the pipe + that sys_popen open. +arguments: iFd, an integer that indicates a file handle. +return value: An integer that indicates the final status of the process working before. +note: none +date: 2022/8/2 +contact tel: same +*/ +int sys_pclose(s32 iFd) +{ + struct PIPE_ID* pstCur = NULL; + s32 iStat = -1; + pid_t pid; + + pstCur = bbox_FindPid(iFd); + if (pstCur == NULL) { + return -1; + } + + sys_close(iFd); + + do { + pid = sys_waitpid(pstCur->pid, &iStat, 0); + } while (pid == -1 && errno == EINTR); + + bbox_PutPid(pstCur); + return (pid == -1 ? -1 : iStat); +} + +/* +function name: bbox_listdir +description: The function list all files below this path in directory. +arguments: The first argument is a pointer to a string representing a file path, all files below + this path will be listed in directory. The second argument is a pointer to a callback + function. The last is a pointer of type void*, it indicates a command line. +return value: An integer that indicates the result of function, if normal, it's RET_OK, else + it's RET_ERR. +note: The path that the first argument represents should be absolute path, take care. +date: 2022/8/2 +contact tel: same +*/ +s32 bbox_listdir(const char* pstPath, BBOX_LIST_DIR_CALLBACK callback, void* pArgs) +{ + struct linux_dirent* pstEntry = NULL; + s32 iDir; + char szBuff[PAGE_SIZE]; + ssize_t nBytes; + s32 iRet = RET_OK; + + if (callback == NULL || pstPath == NULL) { + return RET_ERR; + } + + iDir = sys_open(pstPath, O_RDONLY | O_DIRECTORY, 0); + if (iDir < 0) { + bbox_print(PRINT_ERR, "open directory failed, errno = %d\n", errno); + return RET_ERR; + } + + /* get the file in directory */ + do { + nBytes = sys_getdents(iDir, (struct linux_dirent*)szBuff, sizeof(szBuff)); + if (nBytes < 0) { + bbox_print(PRINT_ERR, "get directory ents failed, errno = %d\n", errno); + sys_close(iDir); + return RET_ERR; + } else if (nBytes == 0) { + /* break when there is no file not read */ + break; + } + + for (pstEntry = (struct linux_dirent*)szBuff; + (pstEntry < (struct linux_dirent*)&szBuff[nBytes]) && iRet == RET_OK; + pstEntry = (struct linux_dirent*)((char*)pstEntry + pstEntry->d_reclen)) { + if (pstEntry->d_ino == 0) { + continue; + } + + iRet = callback(pstPath, pstEntry->d_name, pArgs); + } + } while (nBytes > 0 && iRet == RET_OK); + + sys_close(iDir); + + return iRet; +} -- 2.34.1 From c11087954eaf00b9491439c00d3842b0a7a59625 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:59:18 +0800 Subject: [PATCH 26/56] Delete 'src/gausskernel/cbb/bbox/bbox_print.cpp' --- src/gausskernel/cbb/bbox/bbox_print.cpp | 409 ------------------------ 1 file changed, 409 deletions(-) delete mode 100644 src/gausskernel/cbb/bbox/bbox_print.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_print.cpp b/src/gausskernel/cbb/bbox/bbox_print.cpp deleted file mode 100644 index ae7be6486..000000000 --- a/src/gausskernel/cbb/bbox/bbox_print.cpp +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * bbox_print.cpp - * - * IDENTIFICATION - * src/gausskernel/cbb/bbox/bbox_print.cpp - * - * ------------------------------------------------------------------------- - */ -#include -#include "bbox_syscall_support.h" -#include "bbox_print.h" -#include "../../src/include/securec.h" -#include "../../src/include/securec_check.h" - -char g_acBBoxLog[BBOX_LOG_SIZE]; -char* g_pcCurWriteLogPos = g_acBBoxLog; -int g_iLastLogLen = BBOX_LOG_SIZE; - -static EN_PRINT_TYPE g_enLogLevel = PRINT_LOG; -static EN_PRINT_TYPE g_enScreenLogLeven = PRINT_TIP; - -static int g_iLogScreen = 0; -static int g_isLogInitialed = 0; - -typedef int (*BBOX_vnprintCallBack)(char c, void* pPtr, s32* piCount, s32 iSize); - -/* - * init log - */ -void bbox_initlog(int iLogScreen) -{ - if (g_isLogInitialed) { - return; - } - - errno_t rc = memset_s(g_acBBoxLog, sizeof(g_acBBoxLog), 0, sizeof(g_acBBoxLog)); - securec_check_c(rc, "\0", "\0"); - g_pcCurWriteLogPos = g_acBBoxLog; - g_iLastLogLen = BBOX_LOG_SIZE; - g_iLogScreen = (iLogScreen) ? 1 : 0; - - g_isLogInitialed = 1; -} - -/* -function name: bbox_itoc -description: Convert an integer to a character. -arguments: An integer needed to be converted. -return value: An character that corresponds to the function's integer argument. -note: The integer argument can be converted in radices more than decimalism. -date: 2022/8/2 -contact tel: 18720816902 -*/ -inline char bbox_itoc(u8 sNum) -{ - return (char)((sNum < 10) ? (sNum + 48) : (sNum + 87)); -} - -/* -function name: bbox_put_dox -description: Conversion of number systems. -arguments: The first argument pCallback is a pointer to a callback function, we - use it to reverse the final result. The second argument is a pointer of - type void* used as a argument of function pCallback. The third argument - piCount is a pointer of type int, an offset pointer, also be used as a argument - of pCallback. The fourth argument is an integer of 32 bits, it indicates the buffer - size pCallback uses.The fifth argument uNum is a decimal integer that will - be converted to an integer in another radix. The sixth argument is used as - base to conversion of number systems. The last argument indicates the integer - after converted is a negative integer or not. -return value: An integer, indicating if the function pCallback work successfully. -note: The argument uNum should be a positive integer, after conversion of number systems - the sign will be appended to string's tail. -date: 2022/8/2 -contact tel: 18720816902 -*/ -s32 bbox_put_dox(BBOX_vnprintCallBack pCallback, void* ptr, s32* piCount, u32 iSize, u64 uNum, s32 sSys, s32 isNeg) -{ - s64 i = 0; - s64 j = 0; - char Tmp[32]; - s32 iRet = 0; - - if (0 == uNum) { - return pCallback('0', ptr, piCount, iSize); - } - - /* convert */ - while (uNum) { - j = uNum % sSys; - Tmp[i] = bbox_itoc((u8)j); - uNum /= sSys; - i++; - } - - /* is negative */ - if (isNeg) { - Tmp[i] = '-'; - i++; - } - - /* reverse copy result */ - i--; - while (i >= 0 && iRet == 0) { - iRet = pCallback(Tmp[i], ptr, piCount, iSize); - i--; - } - - return iRet; -} - -/* -function name: bbox_vsnprintf -description: The function is used to print string in corresponding array. -arguments: The first argument is a pointer to a callback function, the next is a - pointer to private data to call this function, also to buffer. - The third is used to destine buffer size. The forth is used to destine - the print format of deferent string, the last is a pointer to variable parameter list. -return value: An integer, if iSize is big enough, then the return value is the length of - string been written in destined memory successfully, not include '\0', - if function makes errors, the return value is a negative integer. -note: none -date: 2022/8/3 -contact tel: 18720816902 -*/ -s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const char* pFmt, va_list ap) -{ - - s32 iCount = 0; - char c; - s32 iCheckFmt = 0; - s32 iQualifier = 0; - s32 iSizeTConv = 0; - s32 iRet = 0; - - if (iSize <= 0) { - return 0; - } - - /* traversal handles formatting strings */ - while (0 == iRet) { - c = *(pFmt++); - if (!c) { - break; - } - /* judge format type if % */ - if (c == '%' && 0 == iCheckFmt) { - iQualifier = 0; - iSizeTConv = 0; - iCheckFmt = 1; - continue; - } else if (0 == iCheckFmt) { - /* copy */ - iRet = pCallback(c, ptr, &iCount, iSize); - continue; - } - - /* check whether the parameter has l */ - if (c == 'l' && iQualifier == 0) { - iQualifier = 1; - continue; - } else if (c == 'z') { - iSizeTConv = 1; - continue; - } - - switch (c) { - case 'c': { - char ch = (char)va_arg(ap, int); - iRet = pCallback(ch, ptr, &iCount, iSize); - } break; - case 'd': { - signed long long n = 0; - if (iSizeTConv) { -#if (defined(__x86_64__)) || (defined(__aarch64__)) - n = va_arg(ap, signed long int); -#else - n = va_arg(ap, signed int); -#endif - } else { - n = (iQualifier) ? va_arg(ap, signed long int) : va_arg(ap, signed int); - } - - s32 isNeg = (n < 0) ? 1 : 0; - n = (isNeg) ? (-1 * n) : (n); - iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, isNeg); - } break; - case 'l': { - signed long long n = (iQualifier) ? va_arg(ap, long long) : va_arg(ap, long); - s32 isNeg = (n < 0) ? 1 : 0; - n = (isNeg) ? (-1 * n) : (n); - iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, isNeg); - } break; - case 'x': { - unsigned long long n = (iQualifier) ? va_arg(ap, unsigned long int) : va_arg(ap, unsigned int); - iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 16, 0); - } break; - case 'u': { - unsigned long long n = 0; - if (iSizeTConv) { -#if (defined(__x86_64__)) || (defined(__aarch64__)) - n = va_arg(ap, unsigned long long); -#else - n = va_arg(ap, unsigned int); -#endif - } else { - n = (iQualifier) ? va_arg(ap, unsigned long long) : va_arg(ap, unsigned int); - } - - iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, 0); - } break; - case 'p': { - unsigned long long n = va_arg(ap, unsigned long); - iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 16, 0); - } break; - case 's': { - char* p = va_arg(ap, char*); - - if (p == NULL) { - p = ""; - } - while (*p && (!iRet)) { - iRet = pCallback(*p, ptr, &iCount, iSize); - p++; - } - } break; - default: - iRet = pCallback(c, ptr, &iCount, iSize); - break; - } - - iQualifier = 0; - iCheckFmt = 0; - } - - if (iRet != RET_OK || pCallback(0, ptr, &iCount, iSize) != RET_OK) { - return RET_ERR; - } - - return iCount; -} - -/* -function name: bbox_SnprintCallback -description: The function is used to print string in corresponding array, usually - used as the first argument of function bbox_vsnprintf. -arguments: The first argument is a character waited to be written into buffer that - pPtr directs, the second argument directs a buffer area, the third is a - pointer to an integera used to record the count to call this callback function, - at the same time, it represents the count of characters written into buffer, it's - a pointer so that we can conveniently modify data storedin it. The last - argument destines the size of buffer, it represents the limit of length. -return value: An integer, if written successfully, it's RET_OK, else it's RET_ERR. -note: none -date: 2022/8/3 -contact tel: 18720816902 -*/ -s32 bbox_SnprintCallback(char c, void* pPtr, s32* piCount, s32 iSize) -{ - char** pszBuff = (char**)pPtr; - - /* return if the buffer length is exceeded */ - if (*piCount >= iSize - 1) { - /* set the last bit to 0 and return err, means that exit snprintf_s function. */ - **pszBuff = 0; - return RET_ERR; - } - - **pszBuff = c; - (*pszBuff)++; - (*piCount)++; - - return RET_OK; -} - -/* - * simple signal-safe function snprintf_s - * in : pstBuff - buffer pointer - * iSize - buffer size - * pFmt - string format - * return : string length - */ -s32 bbox_snprintf(char* pszBuff, s32 iSize, const char* pFmt, ...) -{ - va_list ap; - s32 iRet = 0; - - va_start(ap, pFmt); - iRet = bbox_vsnprintf(bbox_SnprintCallback, &pszBuff, iSize, pFmt, ap); - va_end(ap); - - return iRet; -} - -/* call back function of printf - * in : c - charactor to calculate - * pPtr - buffer pointer - * piCount - count of character read - * iSize - length limit - * return : string length - */ -s32 bbox_PrintCallback(char c, void* pPtr, s32* piCount, s32 iSize) -{ - s32* fd = (s32*)pPtr; - - if ('\0' == c) { - return RET_OK; - } - - /* write */ - if (fd != 0 && *fd >= 0) { - sys_write(*fd, &c, 1); - } - - /* return if the buffer length is exceeded */ - if (g_iLastLogLen <= 1) { - /* set the last bit to 0 and return err, means that exit snprintf_s function. */ - *g_pcCurWriteLogPos = 0; - if (g_iLogScreen) { - return RET_OK; - } - - return RET_ERR; - } - - *g_pcCurWriteLogPos = c; - (g_pcCurWriteLogPos)++; - (g_iLastLogLen)--; - - return RET_OK; -} - -/* - * simple signal-safe function printf - */ -void bbox_printf(const char* pFmt, ...) -{ - s32 fd = 1; - va_list ap; - - va_start(ap, pFmt); - (void)bbox_vsnprintf(bbox_PrintCallback, &fd, 0xFFFF, pFmt, ap); - va_end(ap); -} - -/* - * simple signal-safe function print - */ -void bbox_print(EN_PRINT_TYPE enType, const char* pFmt, ...) -{ - s32 fd = 1; - va_list ap; - - if (enType < g_enLogLevel) { - return; - } - - if (g_iLogScreen && enType >= g_enScreenLogLeven) { - fd = 1; - } else { - fd = -1; - } - - va_start(ap, pFmt); - (void)bbox_vsnprintf(bbox_PrintCallback, &fd, 0xFFFF, pFmt, ap); - va_end(ap); -} - -/* - * set print level - */ -s32 bbox_set_log_level(EN_PRINT_TYPE enLevel) -{ - if (enLevel < PRINT_DBG || enLevel > PRINT_ERR) { - return RET_ERR; - } - - g_enLogLevel = enLevel; - - return RET_OK; -} - -/* - * set screen print level - */ -s32 bbox_set_screen_log_level(EN_PRINT_TYPE enLevel) -{ - if (enLevel < PRINT_DBG || enLevel > PRINT_ERR) { - return RET_ERR; - } - - g_enScreenLogLeven = enLevel; - - return RET_OK; -} -- 2.34.1 From 0479fb1064bb64e8ce473815ebe90ffe098a08b0 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 20:59:42 +0800 Subject: [PATCH 27/56] ADD file via upload --- src/gausskernel/cbb/bbox/bbox_print.cpp | 436 ++++++++++++++++++++++++ 1 file changed, 436 insertions(+) create mode 100644 src/gausskernel/cbb/bbox/bbox_print.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_print.cpp b/src/gausskernel/cbb/bbox/bbox_print.cpp new file mode 100644 index 000000000..9521431c2 --- /dev/null +++ b/src/gausskernel/cbb/bbox/bbox_print.cpp @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * bbox_print.cpp + * + * IDENTIFICATION + * src/gausskernel/cbb/bbox/bbox_print.cpp + * + * ------------------------------------------------------------------------- + */ +#include +#include "bbox_syscall_support.h" +#include "bbox_print.h" +#include "../../src/include/securec.h" +#include "../../src/include/securec_check.h" + +char g_acBBoxLog[BBOX_LOG_SIZE]; +char* g_pcCurWriteLogPos = g_acBBoxLog; +int g_iLastLogLen = BBOX_LOG_SIZE; + +static EN_PRINT_TYPE g_enLogLevel = PRINT_LOG; +static EN_PRINT_TYPE g_enScreenLogLeven = PRINT_TIP; + +static int g_iLogScreen = 0; +static int g_isLogInitialed = 0; + +typedef int (*BBOX_vnprintCallBack)(char c, void* pPtr, s32* piCount, s32 iSize); + +/* + * init log + */ +void bbox_initlog(int iLogScreen) +{ + if (g_isLogInitialed) { + return; + } + + errno_t rc = memset_s(g_acBBoxLog, sizeof(g_acBBoxLog), 0, sizeof(g_acBBoxLog)); + securec_check_c(rc, "\0", "\0"); + g_pcCurWriteLogPos = g_acBBoxLog; + g_iLastLogLen = BBOX_LOG_SIZE; + g_iLogScreen = (iLogScreen) ? 1 : 0; + + g_isLogInitialed = 1; +} + +/* +function name: bbox_itoc +description: Convert an integer to a character. +arguments: An integer needed to be converted. +return value: An character that corresponds to the function's integer argument. +note: The integer argument can be converted in radices more than decimalism. +date: 2022/8/2 +contact tel: 18720816902 +*/ +inline char bbox_itoc(u8 sNum) +{ + return (char)((sNum < 10) ? (sNum + 48) : (sNum + 87)); +} + +/* +function name: bbox_put_dox +description: Conversion of number systems. +arguments: The first argument pCallback is a pointer to a callback function, we + use it to reverse the final result. The second argument is a pointer of + type void* used as a argument of function pCallback. The third argument + piCount is a pointer of type int, an offset pointer, also be used as a argument + of pCallback. The fourth argument is an integer of 32 bits, it indicates the buffer + size pCallback uses.The fifth argument uNum is a decimal integer that will + be converted to an integer in another radix. The sixth argument is used as + base to conversion of number systems. The last argument indicates the integer + after converted is a negative integer or not. +return value: An integer, indicating if the function pCallback work successfully. +note: The argument uNum should be a positive integer, after conversion of number systems + the sign will be appended to string's tail. +date: 2022/8/2 +contact tel: 18720816902 +*/ +s32 bbox_put_dox(BBOX_vnprintCallBack pCallback, void* ptr, s32* piCount, u32 iSize, u64 uNum, s32 sSys, s32 isNeg) +{ + s64 i = 0; + s64 j = 0; + char Tmp[32]; + s32 iRet = 0; + + if (0 == uNum) { + return pCallback('0', ptr, piCount, iSize); + } + + /* convert */ + while (uNum) { + j = uNum % sSys; + Tmp[i] = bbox_itoc((u8)j); + uNum /= sSys; + i++; + } + + /* is negative */ + if (isNeg) { + Tmp[i] = '-'; + i++; + } + + /* reverse copy result */ + i--; + while (i >= 0 && iRet == 0) { + iRet = pCallback(Tmp[i], ptr, piCount, iSize); + i--; + } + + return iRet; +} + +/* +function name: bbox_vsnprintf +description: The function is used to print string in corresponding array. +arguments: The first argument is a pointer to a callback function, the next is a + pointer to private data to call this function, also to buffer. + The third is used to destine buffer size. The forth is used to destine + the print format of deferent string, the last is a pointer to variable parameter list. +return value: An integer, if iSize is big enough, then the return value is the length of + string been written in destined memory successfully, not include '\0', + if function makes errors, the return value is a negative integer. +note: none +date: 2022/8/3 +contact tel: 18720816902 +*/ +s32 bbox_vsnprintf(BBOX_vnprintCallBack pCallback, void* ptr, s32 iSize, const char* pFmt, va_list ap) +{ + // 定义变量 + s32 iCount = 0; // 记录写入缓冲区的字符数 + char c; // 临时存储格式化字符串中的字符 + s32 iCheckFmt = 0; // 判断是否处于格式化标识符 '%' 的状态 + s32 iQualifier = 0; // 判断是否有 'l' 或 'z' 限定符 + s32 iSizeTConv = 0; // 判断是否有 'z' 限定符 + s32 iRet = 0; // 用于保存回调函数的返回值 + + // 检查缓冲区大小是否合法 + if (iSize <= 0) { + return 0; + } + + // 遍历格式化字符串 + while (0 == iRet) { + c = *(pFmt++); + + // 判断是否遍历完格式化字符串 + if (!c) { + break; + } + + // 判断是否为格式化标识符 '%' + if (c == '%' && 0 == iCheckFmt) { + // 初始化限定符和转换说明符 + iQualifier = 0; + iSizeTConv = 0; + iCheckFmt = 1; + continue; + } else if (0 == iCheckFmt) { + // 复制非格式化标识符 + iRet = pCallback(c, ptr, &iCount, iSize); + continue; + } + + // 判断是否有 'l' 限定符或 'z' 限定符 + if (c == 'l' && iQualifier == 0) { + iQualifier = 1; // 标记有 'l' 限定符 + continue; + } else if (c == 'z') { + iSizeTConv = 1; // 标记有 'z' 限定符 + continue; + } + + // 根据格式化标识符的类型执行相应的操作 + switch (c) { + case 'c': { + // 处理字符类型 + char ch = (char)va_arg(ap, int); + iRet = pCallback(ch, ptr, &iCount, iSize); // 调用回调函数处理字符 + } break; + case 'd': { + // 处理有符号十进制整数类型 + signed long long n = 0; + if (iSizeTConv) { +#if (defined(__x86_64__)) || (defined(__aarch64__)) + n = va_arg(ap, signed long int); +#else + n = va_arg(ap, signed int); +#endif + } else { + n = (iQualifier) ? va_arg(ap, signed long int) : va_arg(ap, signed int); + } + + s32 isNeg = (n < 0) ? 1 : 0; + n = (isNeg) ? (-1 * n) : (n); + iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, isNeg); // 调用回调函数处理数字 + } break; + case 'l': { + // 处理长整型类型 + signed long long n = (iQualifier) ? va_arg(ap, long long) : va_arg(ap, long); + s32 isNeg = (n < 0) ? 1 : 0; + n = (isNeg) ? (-1 * n) : (n); + iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, isNeg); // 调用回调函数处理数字 + } break; + case 'x': { + // 处理十六进制整数类型 + unsigned long long n = (iQualifier) ? va_arg(ap, unsigned long int) : va_arg(ap, unsigned int); + iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 16, 0); // 调用回调函数处理数字 + } break; + case 'u': { + // 处理无符号十进制整数类型 + unsigned long long n = 0; + if (iSizeTConv) { +#if (defined(__x86_64__)) || (defined(__aarch64__)) + n = va_arg(ap, unsigned long long); +#else + n = va_arg(ap, unsigned int); +#endif + } else { + n = (iQualifier) ? va_arg(ap, unsigned long long) : va_arg(ap, unsigned int); + } + + iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 10, 0); // 调用回调函数处理数字 + } break; + case 'p': { + // 处理指针类型 + unsigned long long n = va_arg(ap, unsigned long); + iRet = bbox_put_dox(pCallback, ptr, &iCount, iSize, (u64)n, 16, 0); // 调用回调函数处理数字 + } break; + case 's': { + // 处理字符串类型 + char* p = va_arg(ap, char*); + + if (p == NULL) { + p = ""; + } + while (*p && (!iRet)) { + iRet = pCallback(*p, ptr, &iCount, iSize); // 调用回调函数处理字符 + p++; + } + } break; + default: + iRet = pCallback(c, ptr, &iCount, iSize); // 调用回调函数处理字符 + break; + } + + // 重置限定符和转换说明符 + iQualifier = 0; + iCheckFmt = 0; + } + + // 检查回调函数的返回值和终止符的写入情况 + if (iRet != RET_OK || pCallback(0, ptr, &iCount, iSize) != RET_OK) { + return RET_ERR; + } + + return iCount; // 返回写入缓冲区的字符数 +} + +/* +function name: bbox_SnprintCallback +description: The function is used to print string in corresponding array, usually + used as the first argument of function bbox_vsnprintf. +arguments: The first argument is a character waited to be written into buffer that + pPtr directs, the second argument directs a buffer area, the third is a + pointer to an integera used to record the count to call this callback function, + at the same time, it represents the count of characters written into buffer, it's + a pointer so that we can conveniently modify data storedin it. The last + argument destines the size of buffer, it represents the limit of length. +return value: An integer, if written successfully, it's RET_OK, else it's RET_ERR. +note: none +date: 2022/8/3 +contact tel: 18720816902 +*/ +/* +bbox_SnprintCallback函数是一个回调函数,用于处理bbox_snprintf函数中的格式化字符串。该函数的作用是将字符c写入到pszBuff指向的缓冲区中,并更新pszBuff和piCount的值。 +*/ +s32 bbox_SnprintCallback(char c, void* pPtr, s32* piCount, s32 iSize) +{ + char** pszBuff = (char**)pPtr; + + /* 如果超过了缓冲区的长度限制,则返回错误 */ + if (*piCount >= iSize - 1) { + /* 将最后一位设置为0,并返回错误(表示退出snprintf_s函数) */ + **pszBuff = 0; + return RET_ERR; + } + + **pszBuff = c; // 将字符c写入到缓冲区中 + (*pszBuff)++; // 更新pszBuff的地址 + (*piCount)++; // 更新piCount的值 + + return RET_OK; +} + +/* + * simple signal-safe function snprintf_s + * in : pstBuff - buffer pointer + * iSize - buffer size + * pFmt - string format + * return : string length + */ + //bbox_snprintf函数是一个简化版的snprintf函数,用于格式化输出字符串到指定的缓冲区中。 +s32 bbox_snprintf(char* pszBuff, s32 iSize, const char* pFmt, ...) +{ + va_list ap; + s32 iRet = 0; + + va_start(ap, pFmt); + iRet = bbox_vsnprintf(bbox_SnprintCallback, &pszBuff, iSize, pFmt, ap); + va_end(ap); + + return iRet; +} + +/* call back function of printf + * in : c - charactor to calculate + * pPtr - buffer pointer + * piCount - count of character read + * iSize - length limit + * return : string length + */ + /* + bbox_PrintCallback函数是一个回调函数,用于处理bbox_printf函数和bbox_print函数中的格式化字符串。 + 该函数的作用是将字符c写入到指定的文件描述符中,并更新g_pcCurWriteLogPos和g_iLastLogLen的值 + */ +s32 bbox_PrintCallback(char c, void* pPtr, s32* piCount, s32 iSize) +{ + s32* fd = (s32*)pPtr; + + if ('\0' == c) { + return RET_OK; + } + + /* 写入文件描述符 */ + if (fd != 0 && *fd >= 0) { + sys_write(*fd, &c, 1); + } + + /* 如果超过了缓冲区的长度限制,则返回错误 */ + if (g_iLastLogLen <= 1) { + /* 将最后一位设置为0,并返回错误(表示退出snprintf_s函数) */ + *g_pcCurWriteLogPos = 0; + if (g_iLogScreen) { + return RET_OK; + } + + return RET_ERR; + } + + *g_pcCurWriteLogPos = c; // 将字符c写入到缓冲区中 + (g_pcCurWriteLogPos)++; // 更新g_pcCurWriteLogPos的地址 + (g_iLastLogLen)--; // 更新g_iLastLogLen的值 + + return RET_OK; +} + +/* + * simple signal-safe function printf + */ + //bbox_printf函数是一个简化版的printf函数,用于将格式化的字符串输出到标准输出。 +void bbox_printf(const char* pFmt, ...) +{ + s32 fd = 1; + va_list ap; + + va_start(ap, pFmt); + (void)bbox_vsnprintf(bbox_PrintCallback, &fd, 0xFFFF, pFmt, ap); + va_end(ap); +} + +/* + * simple signal-safe function print + */ + //bbox_print函数是一个简化版的printf函数,可以根据打印级别和屏幕打印级别来输出格式化的字符串到标准输出。 +void bbox_print(EN_PRINT_TYPE enType, const char* pFmt, ...) +{ + s32 fd = 1; + va_list ap; + + if (enType < g_enLogLevel) { + return; + } + + if (g_iLogScreen && enType >= g_enScreenLogLeven) { + fd = 1; + } else { + fd = -1; + } + + va_start(ap, pFmt); + (void)bbox_vsnprintf(bbox_PrintCallback, &fd, 0xFFFF, pFmt, ap); + va_end(ap); +} + +/* + * set print level + */ + //bbox_set_log_level函数用于设置日志级别,根据传入的enLevel参数来设置全局变量g_enLogLevel的值。 +s32 bbox_set_log_level(EN_PRINT_TYPE enLevel) +{ + if (enLevel < PRINT_DBG || enLevel > PRINT_ERR) { + return RET_ERR; + } + + g_enLogLevel = enLevel; + + return RET_OK; +} + +/* + * set screen print level + */ + //bbox_set_screen_log_level函数用于设置屏幕打印级别,根据传入的enLevel参数来设置全局变量g_enScreenLogLeven的值。 +s32 bbox_set_screen_log_level(EN_PRINT_TYPE enLevel) +{ + if (enLevel < PRINT_DBG || enLevel > PRINT_ERR) { + return RET_ERR; + } + + g_enScreenLogLeven = enLevel; + + return RET_OK; +} -- 2.34.1 From 2d4e46f02bafa82691065baa354a0217fece2972 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 21:00:04 +0800 Subject: [PATCH 28/56] Delete 'src/gausskernel/cbb/bbox/bbox_syscall_support.cpp' --- .../cbb/bbox/bbox_syscall_support.cpp | 599 ------------------ 1 file changed, 599 deletions(-) delete mode 100644 src/gausskernel/cbb/bbox/bbox_syscall_support.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_syscall_support.cpp b/src/gausskernel/cbb/bbox/bbox_syscall_support.cpp deleted file mode 100644 index 86398ece0..000000000 --- a/src/gausskernel/cbb/bbox/bbox_syscall_support.cpp +++ /dev/null @@ -1,599 +0,0 @@ -/* ------------------------------------------------------------------------- - * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * Portions Copyright (c) 2005-2008, Google Inc. - * - * - * bbox_syscall_support.cpp - * - * IDENTIFICATION - * src/gausskernel/cbb/bbox/bbox_syscall_support.cpp - * - * - */ -#include "bbox_syscall_support.h" -#include "../../src/include/securec.h" -#include "../../src/include/securec_check.h" -__syscall0(long, gettid); -__syscall0(long, getpid); -__syscall0(long, getppid); -__syscall0(long, geteuid); -__syscall0(long, getegid); -__syscall1(long, getsid, pid_t, pid); -__syscall2(long, gettimeofday, struct kernel_timeval*, tv, struct kernel_timezone*, tz); -__syscall4(long, ptrace, long, request, long, pid, void*, addr, void*, data); -__syscall2(long, sigaltstack, const stack_t*, uss, const stack_t*, uoss); -__syscall2(long, getpriority, int, which, int, who); -__syscall2(long, getrlimit, unsigned int, resource, struct kernel_rlimit*, rlim); -__syscall1(long, close, int, fd); -__syscall3(long, read, unsigned int, fd, void*, buf, size_t, count); -__syscall3(long, write, unsigned int, fd, const void*, buf, size_t, count); -__syscall3(long, lseek, unsigned int, fd, off_t, offset, unsigned int, origin); -__syscall3(long, fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg); -__syscall2(long, fstat, int, fd, struct kernel_stat*, statbuf); -__syscall1(long, dup, unsigned int, fildes); -__syscall2(long, nanosleep, struct kernel_timespec*, rqtp, struct kernel_timespec*, rmtp); -__syscall3(long, execve, char*, name, char**, argv, char**, envp); -__syscall4(long, wait4, pid_t, upid, int*, stat_addr, int, options, struct rusage*, ru); -__syscall2(long, kill, pid_t, pid, int, sig); -__syscall0(long, sched_yield); -__syscall1(long, exit, int, error_code); -__syscall1(long, exit_group, int, error_code); -__syscall5( - long, prctl, int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5); -__syscall4( - long, rt_sigprocmask, int, how, struct kernel_sigset_t*, set, struct kernel_sigset_t*, oset, size_t, sigsetsize); -__syscall4(long, rt_sigaction, int, sig, const struct kernel_sigaction*, act, struct kernel_sigaction*, oact, size_t, - sigsetsize); - -#if (defined(__aarch64__)) -__syscall1(long, getpgid, long, pid); -__syscall4(long, openat, int, dfd, const char*, filename, int, flags, int, mode); -__syscall3(long, getdents64, unsigned int, fd, struct linux_dirent*, dirent, unsigned int, count); -__syscall4(long, readlinkat, int, dfd, const char*, path, char*, buf, int, bufsiz); -__syscall4(long, newfstatat, int, dfd, const char*, filename, struct kernel_stat*, statbuf, int, flag); -__syscall3(long, mkdirat, int, dfd, const char*, pathname, int, mode); -__syscall3(long, unlinkat, int, dfd, const char*, pathname, int, flag); -__syscall4(long, renameat, int, olddfd, const char*, oldname, int, newdfd, const char*, newname); -__syscall3(long, fchmodat, int, dfd, const char*, filename, mode_t, mode); -__syscall2(long, pipe2, int*, fildes, int, flags); -__syscall3(long, dup3, unsigned int, oldfd, unsigned int, newfd, int, flags); -__syscall5( - long, clone, unsigned long, clone_flags, unsigned long, newsp, int*, parent_tid, void*, newtls, int*, child_tid); - -long SYS_NAME(getpgrp)(void) -{ - return sys_getpgid(sys_getpid()); -} - -long SYS_NAME(open)(const char* filename, int flags, int mode) -{ - return sys_openat(AT_FDCWD, filename, flags, mode); -} - -long SYS_NAME(getdents)(unsigned int fd, struct linux_dirent* dirent, unsigned int count) -{ - return sys_getdents64(fd, dirent, count); -} - -long SYS_NAME(readlink)(const char* path, char* buf, int bufsiz) -{ - return sys_readlinkat(AT_FDCWD, path, buf, bufsiz); -} - -long SYS_NAME(stat)(char* filename, struct kernel_stat* statbuf) -{ - return sys_newfstatat(AT_FDCWD, filename, statbuf, 0); -} - -long SYS_NAME(mkdir)(const char* pathname, int mode) -{ - return sys_mkdirat(AT_FDCWD, pathname, mode); -} - -long SYS_NAME(unlink)(const char* pathname) -{ - return sys_unlinkat(AT_FDCWD, pathname, 0); -} - -long SYS_NAME(rename)(const char* oldname, const char* newname) -{ - return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname); -} - -long SYS_NAME(chmod)(const char* filename, mode_t mode) -{ - return sys_fchmodat(AT_FDCWD, filename, mode); -} - -long SYS_NAME(pipe)(int* fildes) -{ - return sys_pipe2(fildes, 0); -} - -long SYS_NAME(dup2)(unsigned int oldfd, unsigned int newfd) -{ - return sys_dup3(oldfd, newfd, 0); -} - -long SYS_NAME(fork)(void) -{ - return sys_clone(SIGCHLD, 0, 0, NULL, NULL); -} - -#else - -__syscall0(long, getpgrp); -__syscall3(long, open, const char*, filename, int, flags, int, mode); -__syscall3(long, getdents, unsigned int, fd, struct linux_dirent*, dirent, unsigned int, count); -__syscall3(long, readlink, const char*, path, char*, buf, int, bufsiz); -__syscall2(long, stat, char*, filename, struct kernel_stat*, statbuf); -__syscall2(long, mkdir, const char*, pathname, int, mode); -__syscall1(long, unlink, const char*, pathname); -__syscall2(long, rename, const char*, oldname, const char*, newname); -__syscall2(long, chmod, const char*, filename, mode_t, mode); -__syscall1(long, pipe, int*, fildes); -__syscall2(long, dup2, unsigned int, oldfd, unsigned int, newfd); -__syscall0(long, vfork); -__syscall0(long, fork); - -#endif - -int SYS_NAME(sysconf)(int name) -{ - switch (name) { - case _SC_PAGESIZE: - return getpagesize(); - case _SC_OPEN_MAX: { - struct kernel_rlimit limit = {0}; - if (sys_getrlimit(RLIMIT_NOFILE, &limit) >= 0) { - return limit.rlim_cur; - } else { - /* Default maximum open files for per process */ - return 8192; - } - } - default: - errno = ENOSYS; - return -1; - } -} - -int SYS_NAME(sigemptyset)(struct kernel_sigset_t* set) -{ - errno_t rc = memset_s(set->sig, sizeof(set->sig), 0, sizeof(set->sig)); - securec_check_c(rc, "\0", "\0"); - return 0; -} - -int SYS_NAME(sigfillset)(struct kernel_sigset_t* set) -{ - errno_t rc = memset_s(set->sig, sizeof(set->sig), 0xFF, sizeof(set->sig)); - securec_check_c(rc, "\0", "\0"); - return 0; -} - -int SYS_NAME(sigaddset)(struct kernel_sigset_t* set, int __signum) -{ - int signo = (int)(8 * sizeof(set->sig)); - - if (__signum < 1 || __signum > signo) { - errno = EINVAL; - return -1; - } else { - set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] |= 1UL << ((__signum - 1) % (8 * sizeof(set->sig[0]))); - return 0; - } -} - -int SYS_NAME(sigdelset)(struct kernel_sigset_t* set, int __signum) -{ - int signo = (int)(8 * sizeof(set->sig)); - - if (__signum < 1 || __signum > signo) { - errno = EINVAL; - return -1; - } else { - set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] &= ~(1UL << ((__signum - 1) % (8 * sizeof(set->sig[0])))); - return 0; - } -} - -int SYS_NAME(sigismember)(struct kernel_sigset_t* set, int __signum) -{ - int signo = (int)(8 * sizeof(set->sig)); - - if (__signum < 1 || __signum > signo) { - errno = EINVAL; - return -1; - } else { - return !!(set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] & - (1UL << ((__signum - 1) % (8 * sizeof(set->sig[0]))))); - } -} - -long SYS_NAME(sigprocmask)(int how, struct kernel_sigset_t* set, struct kernel_sigset_t* oldset) -{ - long ret = 0; - - ret = SYS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG + 7) / 8); - return ret; -} - -#if defined(__x86_64__) -__syscall3(long, socket, int, family, int, type, int, protocol); -__syscall4(long, clone, unsigned long, clone_flags, unsigned long, newsp, int*, parent_tidptr, int*, child_tidptr); -long SYS_NAME(waitpid)(pid_t pid, int* status, int options) -{ - return SYS_NAME(wait4)(pid, status, options, 0); -} - -long SYS_NAME(signal)(int __signum, void (*handler)(int)) -{ - struct kernel_sigaction sa; - - errno_t rc = memset_s(&sa, sizeof(sa), 0, sizeof(sa)); - securec_check_c(rc, "\0", "\0"); - - sys_sigfillset(&sa.sa_mask); - sa.sa_flags |= SA_RESTORER | SA_RESTART; - sa.handle.sa_handler_ = handler; - - return SYS_NAME(rt_sigaction)(__signum, &sa, NULL, (KERNEL_NSIG + 7) / 8); -} - -#define CLONE_SYSCALL_X86_64 \ - "movq %2,%%rax\n" \ - "syscall\n" \ - "testq %%rax,%%rax\n" \ - "jnz 1f\n" \ - "xorq %%rbp,%%rbp\n" \ - "popq %%rax\n" \ - "popq %%rdi\n" \ - "call *%%rax\n" \ - "movq %%rax,%%rdi\n" \ - "movq %3,%%rax\n" \ - "syscall\n" - -long SYS_NAME(_clone)( - int (*fn)(void*), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr) -{ - long ___res; - { - register void* __tls __asm__("r8") = newtls; - register int* __ctid __asm__("r10") = child_tidptr; - - __asm__ __volatile__( - - /* example: if (fn == NULL) return -EINVAL; */ - "testq %4,%4\n" - "jz 1f\n" - - /* example: if (child_stack == NULL) return -EINVAL; */ - "testq %5,%5\n" - "jz 1f\n" - - "subq $0x10,%5\n" - - /* Push "arg" and "fn" onto the stack that will be - * used by the child. - */ - "movq %7,0x8(%5)\n" - "movq %4,0x0(%5)\n" - - /* example: %rax = syscall(%rax = __NR_clone, - * %rdi = flags, - * %rsi = child_stack, - * %rdx = parent_tidptr, - * %r8 = new_tls, - * %r10 = child_tidptr) - */ - CLONE_SYSCALL_X86_64 - - /* Return to parent. - */ - "1:\n" - : "=a"(___res) - : "0"(-EINVAL), - "i"(__NR_clone), - "i"(__NR_exit), - "r"(fn), - "S"(child_stack), - "D"(flags), - "r"(arg), - "d"(parent_tidptr), - "r"(__tls), - "r"(__ctid) - : "memory", "r11", "rcx"); - } - ___syscall_return(int, ___res); -} - -#elif (defined(__i386__)) -__syscall3(long, waitpid, pid_t, pid, int*, stat_addr, int, options); -__syscall2(long, signal, int, sig, __sighandler_t, handler); -__syscall2(long, socketcall, int, call, va_list, args); -__syscall5( - long, clone, unsigned long, clone_flags, unsigned long, newsp, int*, parent_tid, void*, newtls, int*, child_tid); - -long do_syscall(int number, ...) -{ - register long result; - __asm__ volatile("push %ebx; push %esi; push %edi"); - __asm__ volatile("mov 28(%%ebp),%%edi;" - "mov 24(%%ebp),%%esi;" - "mov 20(%%ebp),%%edx;" - "mov 16(%%ebp),%%ecx;" - "mov 12(%%ebp),%%ebx;" - "mov 8(%%ebp),%%eax;" - "int $0x80" - : "=a"(result)); - __asm__ volatile("pop %edi; pop %esi; pop %ebx"); - return result; -} - -long SYS_NAME(_socketcall)(int op, ...) -{ - int ret; - ret = 0; - va_list ap; - va_start(ap, op); - ret = SYS_NAME(socketcall)(op, ap); - va_end(ap); - return ret; -} - -long SYS_NAME(socket)(int domain, int type, int protocol) -{ - return SYS_NAME(_socketcall)(1, domain, type, protocol); -} - -#define CLONE_SYSCALL_X86_32 \ - "movl %8,%%esi\n" \ - "movl %5,%%eax\n" \ - "movl %7,%%edx\n" \ - "movl %9,%%edi\n" \ - "pushl %%ebx\n" \ - "movl %%eax,%%ebx\n" \ - "movl %2,%%eax\n" \ - "int $0x80\n" \ - "popl %%ebx\n" \ - "test %%eax,%%eax\n" \ - "jnz 1f\n" \ - "movl $0x0,%%ebp\n" \ - "call *%%ebx\n" \ - "movl %%eax,%%ebx\n" \ - "movl $0x1,%%eax\n" \ - "int $0x80\n" - -long SYS_NAME(_clone)( - int (*fn)(void*), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr) -{ - long ___res; - - __asm__ __volatile__( - /* example: if (fn == NULL) return -EINVAL; */ - "movl %3,%%ecx\n" - "jecxz 1f\n" - - /* example: if (child_stack == NULL) return -EINVAL; */ - "movl %4,%%ecx\n" - "jecxz 1f\n" - - /* Set up alignment of the child stack: - * example: child_stack = (child_stack & ~0xF) - 20; - */ - "andl $-16,%%ecx\n" - "subl $0x14,%%ecx\n" - - /* Push "arg" and "fn" onto the stack that will be - * used by the child. - */ - "movl %6,%%eax\n" - "movl %%eax,4(%%ecx)\n" - "movl %3,%%eax\n" - "movl %%eax,(%%ecx)\n" - - /* example: %eax = syscall(%eax = __NR_clone, - * %ebx = flags, - * %ecx = child_stack, - * %edx = parent_tidptr, - * %esi = newtls, - * %edi = child_tidptr) - * Also, make sure that %ebx gets preserved as it is - * used in PIC mode. - */ - CLONE_SYSCALL_X86_32 - - /* Return to parent. - */ - "1:\n" - : "=a"(___res) - : "0"(-EINVAL), - "i"(__NR_clone), - "m"(fn), - "m"(child_stack), - "m"(flags), - "m"(arg), - "m"(parent_tidptr), - "m"(newtls), - "m"(child_tidptr) - : "memory", "ecx", "edx", "esi", "edi"); - ___syscall_return(int, ___res); -} - -#elif (defined(__ARM_ARCH_5TE__)) || (defined(__ARM_ARCH_7A__)) -__syscall3(long, socket, int, d, int, t, int, p); -__syscall5( - long, clone, unsigned long, clone_flags, unsigned long, newsp, int*, parent_tid, void*, newtls, int*, child_tid); - -long SYS_NAME(waitpid)(pid_t pid, int* status, int options) -{ - return SYS_NAME(wait4)(pid, status, options, 0); -} - -long SYS_NAME(signal)(int __signum, void (*handler)(int)) -{ - struct kernel_sigaction _sa; - struct kernel_sigaction old; - - errno_t rc = memset_s(&_sa, sizeof(_sa), 0, sizeof(_sa)); - securec_check_c(rc, "\0", "\0"); - sys_sigfillset(&_sa.sa_mask); - _sa.sa_flags |= SA_RESTORER | SA_RESTART; - _sa.handle.sa_handler_ = handler; - - return SYS_NAME(rt_sigaction)(__signum, &_sa, &old, (KERNEL_NSIG + 7) / 8); -} - -long SYS_NAME(_clone)( - int (*fn)(void*), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr) -{ - register long ___res __asm__("r5"); - - { - if (fn == NULL || child_stack == NULL) { - ___res = -EINVAL; - goto _clone_exit; - } - - /* stash first 4 arguments on stack first because we can only load - * them after all function calls. - */ - int tmp_flags = flags; - int* tmp_stack = (int*)child_stack; - void* tmp_ptid = parent_tidptr; - void* tmp_tls = newtls; - - register int* ___ctid __asm__("r4") = child_tidptr; - - /* Push "arg" and "fn" onto the stack that will be - * used by the child. - */ - *(--tmp_stack) = (int)arg; - *(--tmp_stack) = (int)fn; - - /* We must load r0..r3 last after all possible function calls. */ - register int ___flags __asm__("r0") = tmp_flags; - register void* ___stack __asm__("r1") = tmp_stack; - register void* ___ptid __asm__("r2") = tmp_ptid; - register void* ___tls __asm__("r3") = tmp_tls; - - /* example: %r0 = syscall(%r0 = flags, - * %r1 = child_stack, - * %r2 = parent_tidptr, - * %r3 = newtls, - * %r4 = child_tidptr) - */ - __SYS_REG(clone) - __asm__ __volatile__( - "push {r7}\n" - "mov r7,%1\n" __syscall(clone) "\n" - - "movs %0,r0\n" - "bne 1f\n" - - "ldr r0,[sp, #4]\n" - "mov lr,pc\n" - "ldr pc,[sp]\n" - - "mov r7,%2\n" __syscall(exit) "\n" - - "1: pop {r7}\n" - : "=r"(___res) - : "r"(__sysreg), "i"(__NR_exit), "r"(___stack), "r"(___flags), "r"(___ptid), "r"(___tls), "r"(___ctid) - : "cc", "lr", "memory"); - } - -_clone_exit: - ___syscall_return(int, ___res); -} - -#elif (defined(__aarch64__)) -__syscall3(long, socket, int, family, int, type, int, protocol); - -long SYS_NAME(waitpid)(pid_t pid, int* status, int options) -{ - return SYS_NAME(wait4)(pid, status, options, 0); -} - -long SYS_NAME(signal)(int __signum, void (*handler)(int)) -{ - struct kernel_sigaction _sa; - struct kernel_sigaction old; - - errno_t rc = memset_s(&_sa, sizeof(_sa), 0, sizeof(_sa)); - securec_check_c(rc, "\0", "\0"); - sys_sigfillset(&_sa.sa_mask); - _sa.sa_flags |= SA_RESTORER | SA_RESTART; - _sa.handle.sa_handler_ = handler; - - return SYS_NAME(rt_sigaction)(__signum, &_sa, &old, (KERNEL_NSIG + 7) / 8); -} - -long SYS_NAME(_clone)( - int (*fn)(void*), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr) -{ - register long __res_x0 __asm__("x0"); - long ___res; - { - register int (*__fn)(void*) __asm__("x0") = fn; - register void* __stack __asm__("x1") = child_stack; - register int __flags __asm__("x2") = flags; - register void* __arg __asm__("x3") = arg; - register int* __ptid __asm__("x4") = parent_tidptr; - register void* __tls __asm__("x5") = newtls; - register int* __ctid __asm__("x6") = child_tidptr; - - __asm__ __volatile__( - - /* example: if (fn == NULL || child_stack == NULL) return -EINVAL; */ - "cbz x0,1f\n" - "cbz x1,1f\n" - - /* Push "arg" and "fn" onto the stack that will be - * used by the child. - */ - "stp x0,x3, [x1, #-16]!\n" - - "mov x0,x2\n" /* flags */ - "mov x2,x4\n" /* ptid */ - "mov x3,x5\n" /* tls */ - "mov x4,x6\n" /* ctid */ - "mov x8,%9\n" /* clone */ - - "svc 0x0\n" - - /* example: if (%r0 != 0) return %r0; */ - "cmp x0, #0\n" - "bne 2f\n" - - /* In the child, now. Call "fn(arg)". - */ - "ldp x1, x0, [sp], #16\n" - "blr x1\n" - - /* example: Call _exit(%r0). - */ - "mov x8, %10\n" - "svc 0x0\n" - "1:\n" - "mov x8, %1\n" - "2:\n" - : "=r"(__res_x0) - : "i"(-EINVAL), - "r"(__fn), - "r"(__stack), - "r"(__flags), - "r"(__arg), - "r"(__ptid), - "r"(__tls), - "r"(__ctid), - "i"(__NR_clone), - "i"(__NR_exit) - : "x30", "memory"); - } - ___res = __res_x0; - ___syscall_return(int, ___res); -} - -#endif -- 2.34.1 From 96fc5d9222a803ce2adbf99b692e433e2a58a091 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 21:00:14 +0800 Subject: [PATCH 29/56] ADD file via upload --- bbox_syscall_support.cpp | 602 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 602 insertions(+) create mode 100644 bbox_syscall_support.cpp diff --git a/bbox_syscall_support.cpp b/bbox_syscall_support.cpp new file mode 100644 index 000000000..ffa0b42cc --- /dev/null +++ b/bbox_syscall_support.cpp @@ -0,0 +1,602 @@ +/* ------------------------------------------------------------------------- + * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * Portions Copyright (c) 2005-2008, Google Inc. + * + * + * bbox_syscall_support.cpp + * + * IDENTIFICATION + * src/gausskernel/cbb/bbox/bbox_syscall_support.cpp + * + * + */ +#include "bbox_syscall_support.h" +#include "../../src/include/securec.h" +#include "../../src/include/securec_check.h" +__syscall0(long, gettid); +__syscall0(long, getpid); +__syscall0(long, getppid); +__syscall0(long, geteuid); +__syscall0(long, getegid); +__syscall1(long, getsid, pid_t, pid); +__syscall2(long, gettimeofday, struct kernel_timeval*, tv, struct kernel_timezone*, tz); +__syscall4(long, ptrace, long, request, long, pid, void*, addr, void*, data); +__syscall2(long, sigaltstack, const stack_t*, uss, const stack_t*, uoss); +__syscall2(long, getpriority, int, which, int, who); +__syscall2(long, getrlimit, unsigned int, resource, struct kernel_rlimit*, rlim); +__syscall1(long, close, int, fd); +__syscall3(long, read, unsigned int, fd, void*, buf, size_t, count); +__syscall3(long, write, unsigned int, fd, const void*, buf, size_t, count); +__syscall3(long, lseek, unsigned int, fd, off_t, offset, unsigned int, origin); +__syscall3(long, fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg); +__syscall2(long, fstat, int, fd, struct kernel_stat*, statbuf); +__syscall1(long, dup, unsigned int, fildes); +__syscall2(long, nanosleep, struct kernel_timespec*, rqtp, struct kernel_timespec*, rmtp); +__syscall3(long, execve, char*, name, char**, argv, char**, envp); +__syscall4(long, wait4, pid_t, upid, int*, stat_addr, int, options, struct rusage*, ru); +__syscall2(long, kill, pid_t, pid, int, sig); +__syscall0(long, sched_yield); +__syscall1(long, exit, int, error_code); +__syscall1(long, exit_group, int, error_code); +__syscall5( + long, prctl, int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5); +__syscall4( + long, rt_sigprocmask, int, how, struct kernel_sigset_t*, set, struct kernel_sigset_t*, oset, size_t, sigsetsize); +__syscall4(long, rt_sigaction, int, sig, const struct kernel_sigaction*, act, struct kernel_sigaction*, oact, size_t, + sigsetsize); + +#if (defined(__aarch64__)) +__syscall1(long, getpgid, long, pid); +__syscall4(long, openat, int, dfd, const char*, filename, int, flags, int, mode); +__syscall3(long, getdents64, unsigned int, fd, struct linux_dirent*, dirent, unsigned int, count); +__syscall4(long, readlinkat, int, dfd, const char*, path, char*, buf, int, bufsiz); +__syscall4(long, newfstatat, int, dfd, const char*, filename, struct kernel_stat*, statbuf, int, flag); +__syscall3(long, mkdirat, int, dfd, const char*, pathname, int, mode); +__syscall3(long, unlinkat, int, dfd, const char*, pathname, int, flag); +__syscall4(long, renameat, int, olddfd, const char*, oldname, int, newdfd, const char*, newname); +__syscall3(long, fchmodat, int, dfd, const char*, filename, mode_t, mode); +__syscall2(long, pipe2, int*, fildes, int, flags); +__syscall3(long, dup3, unsigned int, oldfd, unsigned int, newfd, int, flags); +__syscall5( + long, clone, unsigned long, clone_flags, unsigned long, newsp, int*, parent_tid, void*, newtls, int*, child_tid); + +long SYS_NAME(getpgrp)(void) +{ + return sys_getpgid(sys_getpid()); +} + +long SYS_NAME(open)(const char* filename, int flags, int mode) +{ + return sys_openat(AT_FDCWD, filename, flags, mode); +} + +long SYS_NAME(getdents)(unsigned int fd, struct linux_dirent* dirent, unsigned int count) +{ + return sys_getdents64(fd, dirent, count); +} + +long SYS_NAME(readlink)(const char* path, char* buf, int bufsiz) +{ + return sys_readlinkat(AT_FDCWD, path, buf, bufsiz); +} + +long SYS_NAME(stat)(char* filename, struct kernel_stat* statbuf) +{ + return sys_newfstatat(AT_FDCWD, filename, statbuf, 0); +} + +long SYS_NAME(mkdir)(const char* pathname, int mode) +{ + return sys_mkdirat(AT_FDCWD, pathname, mode); +} + +long SYS_NAME(unlink)(const char* pathname) +{ + return sys_unlinkat(AT_FDCWD, pathname, 0); +} + +long SYS_NAME(rename)(const char* oldname, const char* newname) +{ + return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname); +} + +long SYS_NAME(chmod)(const char* filename, mode_t mode) +{ + return sys_fchmodat(AT_FDCWD, filename, mode); +} + +long SYS_NAME(pipe)(int* fildes) +{ + return sys_pipe2(fildes, 0); +} + +long SYS_NAME(dup2)(unsigned int oldfd, unsigned int newfd) +{ + return sys_dup3(oldfd, newfd, 0); +} + +long SYS_NAME(fork)(void) +{ + return sys_clone(SIGCHLD, 0, 0, NULL, NULL); +} + +#else + +__syscall0(long, getpgrp); +__syscall3(long, open, const char*, filename, int, flags, int, mode); +__syscall3(long, getdents, unsigned int, fd, struct linux_dirent*, dirent, unsigned int, count); +__syscall3(long, readlink, const char*, path, char*, buf, int, bufsiz); +__syscall2(long, stat, char*, filename, struct kernel_stat*, statbuf); +__syscall2(long, mkdir, const char*, pathname, int, mode); +__syscall1(long, unlink, const char*, pathname); +__syscall2(long, rename, const char*, oldname, const char*, newname); +__syscall2(long, chmod, const char*, filename, mode_t, mode); +__syscall1(long, pipe, int*, fildes); +__syscall2(long, dup2, unsigned int, oldfd, unsigned int, newfd); +__syscall0(long, vfork); +__syscall0(long, fork); + +#endif + +int SYS_NAME(sysconf)(int name) +{ + switch (name) { + case _SC_PAGESIZE: + return getpagesize(); // 返回系统页面大小 + case _SC_OPEN_MAX: { + struct kernel_rlimit limit = {0}; // 创建一个kernel_rlimit结构体,并初始化为0 + if (sys_getrlimit(RLIMIT_NOFILE, &limit) >= 0) { // 调用sys_getrlimit函数,将RLIMIT_NOFILE资源限制信息存储在limit中 + return limit.rlim_cur; // 返回当前进程的最大打开文件数 + } else { + /* Default maximum open files for per process */ + return 8192; // 返回默认的最大打开文件数为8192 + } + } + default: + errno = ENOSYS; // 如果name不匹配_SC_PAGESIZE和_SC_OPEN_MAX,则设置errno为ENOSYS表示函数未实现 + return -1; + } +} + +int SYS_NAME(sigemptyset)(struct kernel_sigset_t* set) +{ + errno_t rc = memset_s(set->sig, sizeof(set->sig), 0, sizeof(set->sig)); // 使用memset_s函数将set->sig的值设置为0 + securec_check_c(rc, "\0", "\0"); + return 0; +} + +int SYS_NAME(sigfillset)(struct kernel_sigset_t* set) +{ + errno_t rc = memset_s(set->sig, sizeof(set->sig), 0xFF, sizeof(set->sig)); // 使用memset_s函数将set->sig的值设置为0xFF + securec_check_c(rc, "\0", "\0"); + return 0; +} + +int SYS_NAME(sigaddset)(struct kernel_sigset_t* set, int __signum) +{ + int signo = (int)(8 * sizeof(set->sig)); // 计算消息信号集的位数 + + if (__signum < 1 || __signum > signo) { + errno = EINVAL; // 如果__signum小于1或大于signo,设置errno为EINVAL表示无效参数 + return -1; + } else { + set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] |= 1UL << ((__signum - 1) % (8 * sizeof(set->sig[0]))); // 将__signum对应的位设置为1 + return 0; + } +} + +int SYS_NAME(sigdelset)(struct kernel_sigset_t* set, int __signum) +{ + int signo = (int)(8 * sizeof(set->sig)); // 计算消息信号集的位数 + + if (__signum < 1 || __signum > signo) { + errno = EINVAL; // 如果__signum小于1或大于signo,设置errno为EINVAL表示无效参数 + return -1; + } else { + set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] &= ~(1UL << ((__signum - 1) % (8 * sizeof(set->sig[0])))); // 将__signum对应的位设置为0 + return 0; + } +} + +int SYS_NAME(sigismember)(struct kernel_sigset_t* set, int __signum) +{ + int signo = (int)(8 * sizeof(set->sig)); // 计算消息信号集的位数 + + if (__signum < 1 || __signum > signo) { + errno = EINVAL; // 如果__signum小于1或大于signo,设置errno为EINVAL表示无效参数 + return -1; + } else { + return !!(set->sig[(__signum - 1) / (8 * sizeof(set->sig[0]))] & + (1UL << ((__signum - 1) % (8 * sizeof(set->sig[0]))))); // 检查__signum对应的位是否为1,返回结果 + } +} + +long SYS_NAME(sigprocmask)(int how, struct kernel_sigset_t* set, struct kernel_sigset_t* oldset) +{ + long ret = 0; + + ret = SYS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG + 7) / 8); // 调用SYS_NAME(rt_sigprocmask)函数设置信号屏蔽字 + return ret; +} + +#if defined(__x86_64__) +__syscall3(long, socket, int, family, int, type, int, protocol); +__syscall4(long, clone, unsigned long, clone_flags, unsigned long, newsp, int*, parent_tidptr, int*, child_tidptr); +long SYS_NAME(waitpid)(pid_t pid, int* status, int options) +{ + return SYS_NAME(wait4)(pid, status, options, 0); +} + +long SYS_NAME(signal)(int __signum, void (*handler)(int)) +{ + struct kernel_sigaction sa; + + errno_t rc = memset_s(&sa, sizeof(sa), 0, sizeof(sa)); + securec_check_c(rc, "\0", "\0"); + + sys_sigfillset(&sa.sa_mask); + sa.sa_flags |= SA_RESTORER | SA_RESTART; + sa.handle.sa_handler_ = handler; + + return SYS_NAME(rt_sigaction)(__signum, &sa, NULL, (KERNEL_NSIG + 7) / 8); +} + +#define CLONE_SYSCALL_X86_64 \ + "movq %2,%%rax\n" \ + "syscall\n" \ + "testq %%rax,%%rax\n" \ + "jnz 1f\n" \ + "xorq %%rbp,%%rbp\n" \ + "popq %%rax\n" \ + "popq %%rdi\n" \ + "call *%%rax\n" \ + "movq %%rax,%%rdi\n" \ + "movq %3,%%rax\n" \ + "syscall\n" + +long SYS_NAME(_clone)( + int (*fn)(void*), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr) +{ + long ___res; + { + register void* __tls __asm__("r8") = newtls; + register int* __ctid __asm__("r10") = child_tidptr; + + __asm__ __volatile__( + + /* example: if (fn == NULL) return -EINVAL; */ + "testq %4,%4\n" + "jz 1f\n" + + /* example: if (child_stack == NULL) return -EINVAL; */ + "testq %5,%5\n" + "jz 1f\n" + + "subq $0x10,%5\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movq %7,0x8(%5)\n" + "movq %4,0x0(%5)\n" + + /* example: %rax = syscall(%rax = __NR_clone, + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + CLONE_SYSCALL_X86_64 + + /* Return to parent. + */ + "1:\n" + : "=a"(___res) + : "0"(-EINVAL), + "i"(__NR_clone), + "i"(__NR_exit), + "r"(fn), + "S"(child_stack), + "D"(flags), + "r"(arg), + "d"(parent_tidptr), + "r"(__tls), + "r"(__ctid) + : "memory", "r11", "rcx"); + } + ___syscall_return(int, ___res); +} + +#elif (defined(__i386__)) +__syscall3(long, waitpid, pid_t, pid, int*, stat_addr, int, options); +__syscall2(long, signal, int, sig, __sighandler_t, handler); +__syscall2(long, socketcall, int, call, va_list, args); +__syscall5( + long, clone, unsigned long, clone_flags, unsigned long, newsp, int*, parent_tid, void*, newtls, int*, child_tid); + +long do_syscall(int number, ...) +{ + register long result; + __asm__ volatile("push %ebx; push %esi; push %edi"); + __asm__ volatile("mov 28(%%ebp),%%edi;" + "mov 24(%%ebp),%%esi;" + "mov 20(%%ebp),%%edx;" + "mov 16(%%ebp),%%ecx;" + "mov 12(%%ebp),%%ebx;" + "mov 8(%%ebp),%%eax;" + "int $0x80" + : "=a"(result)); + __asm__ volatile("pop %edi; pop %esi; pop %ebx"); + return result; +} + +long SYS_NAME(_socketcall)(int op, ...) +{ + int ret; + ret = 0; + va_list ap; + va_start(ap, op); + ret = SYS_NAME(socketcall)(op, ap); + va_end(ap); + return ret; +} + +long SYS_NAME(socket)(int domain, int type, int protocol) +{ + return SYS_NAME(_socketcall)(1, domain, type, protocol); +} + +#define CLONE_SYSCALL_X86_32 \ + "movl %8,%%esi\n" \ + "movl %5,%%eax\n" \ + "movl %7,%%edx\n" \ + "movl %9,%%edi\n" \ + "pushl %%ebx\n" \ + "movl %%eax,%%ebx\n" \ + "movl %2,%%eax\n" \ + "int $0x80\n" \ + "popl %%ebx\n" \ + "test %%eax,%%eax\n" \ + "jnz 1f\n" \ + "movl $0x0,%%ebp\n" \ + "call *%%ebx\n" \ + "movl %%eax,%%ebx\n" \ + "movl $0x1,%%eax\n" \ + "int $0x80\n" + +long SYS_NAME(_clone)( + int (*fn)(void*), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr) +{ + long ___res; + + __asm__ __volatile__( + /* example: if (fn == NULL) return -EINVAL; */ + "movl %3,%%ecx\n" + "jecxz 1f\n" + + /* example: if (child_stack == NULL) return -EINVAL; */ + "movl %4,%%ecx\n" + "jecxz 1f\n" + + /* Set up alignment of the child stack: + * example: child_stack = (child_stack & ~0xF) - 20; + */ + "andl $-16,%%ecx\n" + "subl $0x14,%%ecx\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movl %6,%%eax\n" + "movl %%eax,4(%%ecx)\n" + "movl %3,%%eax\n" + "movl %%eax,(%%ecx)\n" + + /* example: %eax = syscall(%eax = __NR_clone, + * %ebx = flags, + * %ecx = child_stack, + * %edx = parent_tidptr, + * %esi = newtls, + * %edi = child_tidptr) + * Also, make sure that %ebx gets preserved as it is + * used in PIC mode. + */ + CLONE_SYSCALL_X86_32 + + /* Return to parent. + */ + "1:\n" + : "=a"(___res) + : "0"(-EINVAL), + "i"(__NR_clone), + "m"(fn), + "m"(child_stack), + "m"(flags), + "m"(arg), + "m"(parent_tidptr), + "m"(newtls), + "m"(child_tidptr) + : "memory", "ecx", "edx", "esi", "edi"); + ___syscall_return(int, ___res); +} + +#elif (defined(__ARM_ARCH_5TE__)) || (defined(__ARM_ARCH_7A__)) +__syscall3(long, socket, int, d, int, t, int, p); +__syscall5( + long, clone, unsigned long, clone_flags, unsigned long, newsp, int*, parent_tid, void*, newtls, int*, child_tid); + +long SYS_NAME(waitpid)(pid_t pid, int* status, int options) +{ + return SYS_NAME(wait4)(pid, status, options, 0); // 调用SYS_NAME(wait4)函数等待子进程结束 +} + +long SYS_NAME(signal)(int __signum, void (*handler)(int)) +{ + struct kernel_sigaction _sa; + struct kernel_sigaction old; + + errno_t rc = memset_s(&_sa, sizeof(_sa), 0, sizeof(_sa)); // 使用memset_s函数将_sa的值设置为0 + securec_check_c(rc, "\0", "\0"); + sys_sigfillset(&_sa.sa_mask); // 将_sa.sa_mask的所有位都设置为1 + _sa.sa_flags |= SA_RESTORER | SA_RESTART; // 设置_sa.sa_flags的标志位 + _sa.handle.sa_handler_ = handler; // 设置_sa.handle.sa_handler_为传入的handler函数 + + return SYS_NAME(rt_sigaction)(__signum, &_sa, &old, (KERNEL_NSIG + 7) / 8); // 调用SYS_NAME(rt_sigaction)函数设置信号处理动作 +} +/* +long SYS_NAME(_clone)(int (fn)(void), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr)函数用于创建一个新的进程,并在新进程中执行指定的函数。 +*/ +long SYS_NAME(_clone)(int (*fn)(void*), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr) +{ + register long ___res __asm__("r5"); + + { + if (fn == NULL || child_stack == NULL) { // 如果传入的函数指针为NULL,或者子进程栈指针为NULL,则返回EINVAL错误码 + ___res = -EINVAL; + goto _clone_exit; + } + + /* stash first 4 arguments on stack first because we can only load + * them after all function calls. + */ + int tmp_flags = flags; // 复制flags的值 `tmp_flags`变量用于保存`flags`的值。 + + int* tmp_stack = (int*)child_stack; // 将子进程栈指针转换为int类型指针并保存为tmp_stack + void* tmp_ptid = parent_tidptr; // 保存parent_tidptr的值 + void* tmp_tls = newtls; // 保存newtls的值 + + register int* ___ctid __asm__("r4") = child_tidptr; // 将child_tidptr保存到___ctid寄存器变量中 + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + *(--tmp_stack) = (int)arg; // 将arg的值存入子进程栈中 + *(--tmp_stack) = (int)fn; // 将fn的值存入子进程栈中 + + /* We must load r0..r3 last after all possible function calls. */ + register int ___flags __asm__("r0") = tmp_flags; // 将tmp_flags保存到___flags寄存器变量中 + register void* ___stack __asm__("r1") = tmp_stack; // 将tmp_stack保存到___stack寄存器变量中 + register void* ___ptid __asm__("r2") = tmp_ptid; // 将tmp_ptid保存到___ptid寄存器变量中 + register void* ___tls __asm__("r3") = tmp_tls; // 将tmp_tls保存到___tls寄存器变量中 + + /* example: %r0 = syscall(%r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = newtls, + * %r4 = child_tidptr) + */ + __SYS_REG(clone) // 定义宏__SYS_REG(clone) + + __asm__ __volatile__( + "push {r7}\n" // 将r7寄存器的值保存到栈中 + "mov r7,%1\n" __syscall(clone) "\n" // 调用系统调用clone + + "movs %0,r0\n" // 将r0的值保存到___res中,并设置条件码 + "bne 1f\n" // 如果条件码不等于0,则跳转到标号1处 + + "ldr r0,[sp, #4]\n" // 将sp加上4,得到地址,然后将该地址处的内容保存到r0寄存器中 + "mov lr,pc\n" // 将pc的值保存到lr寄存器中 + "ldr pc,[sp]\n" // 将sp的值保存到pc寄存器中 + + "mov r7,%2\n" __syscall(exit) "\n" // 调用系统调用exit + + "1: pop {r7}\n" // 将栈中的值保存到r7寄存器中 + : "=r"(___res) // 输出结果保存到___res寄存器变量中 + : "r"(__sysreg), "i"(__NR_exit), "r"(___stack), "r"(___flags), "r"(___ptid), "r"(___tls), "r"(___ctid) // 输入参数 + : "cc", "lr", "memory"); // 修改了条件码,lr寄存器内容以及内存 + } + +_clone_exit: + ___syscall_return(int, ___res); // 调用___syscall_return函数返回结果 +} + +#elif (defined(__aarch64__)) +__syscall3(long, socket, int, family, int, type, int, protocol); + +long SYS_NAME(waitpid)(pid_t pid, int* status, int options) +{ + return SYS_NAME(wait4)(pid, status, options, 0); +} + +long SYS_NAME(signal)(int __signum, void (*handler)(int)) +{ + struct kernel_sigaction _sa; + struct kernel_sigaction old; + + errno_t rc = memset_s(&_sa, sizeof(_sa), 0, sizeof(_sa)); + securec_check_c(rc, "\0", "\0"); + sys_sigfillset(&_sa.sa_mask); + _sa.sa_flags |= SA_RESTORER | SA_RESTART; + _sa.handle.sa_handler_ = handler; + + return SYS_NAME(rt_sigaction)(__signum, &_sa, &old, (KERNEL_NSIG + 7) / 8); +} + +long SYS_NAME(_clone)( + int (*fn)(void*), void* child_stack, int flags, void* arg, int* parent_tidptr, void* newtls, int* child_tidptr) +{ + register long __res_x0 __asm__("x0"); + long ___res; + { + register int (*__fn)(void*) __asm__("x0") = fn; + register void* __stack __asm__("x1") = child_stack; + register int __flags __asm__("x2") = flags; + register void* __arg __asm__("x3") = arg; + register int* __ptid __asm__("x4") = parent_tidptr; + register void* __tls __asm__("x5") = newtls; + register int* __ctid __asm__("x6") = child_tidptr; + + __asm__ __volatile__( + + /* example: if (fn == NULL || child_stack == NULL) return -EINVAL; */ + "cbz x0,1f\n" + "cbz x1,1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "stp x0,x3, [x1, #-16]!\n" + + "mov x0,x2\n" /* flags */ + "mov x2,x4\n" /* ptid */ + "mov x3,x5\n" /* tls */ + "mov x4,x6\n" /* ctid */ + "mov x8,%9\n" /* clone */ + + "svc 0x0\n" + + /* example: if (%r0 != 0) return %r0; */ + "cmp x0, #0\n" + "bne 2f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldp x1, x0, [sp], #16\n" + "blr x1\n" + + /* example: Call _exit(%r0). + */ + "mov x8, %10\n" + "svc 0x0\n" + "1:\n" + "mov x8, %1\n" + "2:\n" + : "=r"(__res_x0) + : "i"(-EINVAL), + "r"(__fn), + "r"(__stack), + "r"(__flags), + "r"(__arg), + "r"(__ptid), + "r"(__tls), + "r"(__ctid), + "i"(__NR_clone), + "i"(__NR_exit) + : "x30", "memory"); + } + ___res = __res_x0; + ___syscall_return(int, ___res); +} + +#endif -- 2.34.1 From d2461962c1eb0ca6d17941c760299e32a7ad0d96 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 21:00:39 +0800 Subject: [PATCH 30/56] Delete 'src/gausskernel/cbb/bbox/bbox_threads.cpp' --- src/gausskernel/cbb/bbox/bbox_threads.cpp | 773 ---------------------- 1 file changed, 773 deletions(-) delete mode 100644 src/gausskernel/cbb/bbox/bbox_threads.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_threads.cpp b/src/gausskernel/cbb/bbox/bbox_threads.cpp deleted file mode 100644 index 4129973cf..000000000 --- a/src/gausskernel/cbb/bbox/bbox_threads.cpp +++ /dev/null @@ -1,773 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * bbox_threads.cpp - * - * IDENTIFICATION - * src/gausskernel/cbb/bbox/bbox_threads.cpp - * - * ------------------------------------------------------------------------- - */ -#include "bbox_syscall_support.h" -#include "bbox_threads.h" -#include "bbox_print.h" -#include "bbox_atomic.h" -#include "../../src/include/securec.h" -#include "../../src/include/securec_check.h" - -static const u32 iSyncSignals[] = { - SIGABRT, - SIGILL, - SIGFPE, - SIGSEGV, - SIGBUS, - SIGXCPU, - SIGXFSZ, -}; - -struct TASK_ATTACH_INFO { - pid_t pid; /* pid */ - u8 cIsAttached; /* Whether the thread is ptraced */ -}; - -struct TASK_CHECK_RESUME_ARGS { - struct TASK_ATTACH_INFO* pstTaskInfo; /* recover process information */ - s32 iThreadCount; /* thread count */ - GET_THREAD_TYPE enType; /* get thread type */ -}; - -struct BBOX_ListParams { - s32 iResult; /* result */ - s32 iError; /* error code */ - u8* pAltStackMem; /* Execution stack information */ - BBOX_GetAllThreadsCallBack pCallBack; /* Executes the callback function exported by the thread */ - BBOX_GetAllThreadDone pDoneCallback; /* call back function after exporting the thread information */ - void* pDoneArgs; /* parameter of call back function pDoneCallback */ - GET_THREAD_TYPE enGetType; /* get data type */ - va_list ap; /* parameter list of function pCallBack */ -}; - -u8 g_szAltStackMem[BBOX_ALT_STACKSIZE]; /* independent thread stack memory */ - -BBOX_ATOMIC_STRU g_isBusy = BBOX_ATOMIC_INIT(0); /* whether deal with core file. */ - -/* -function name: BBOX_ReserveZeroStack -description: The function creat a empty stack, and its size depend on argument count. -arguments: An integer of type s32, namely int, it destines the storage of stack. -return value: void -note: The stack this function creats is actually a character array. -date: 2022/8/3 -contact tel: 18720816902 -*/ -void BBOX_ReserveZeroStack(s32 count) -{ - char buff[count]; - - errno_t rc = memset_s(buff, count, 0, count); - securec_check_c(rc, "\0", "\0"); - - (void)sys_read(-1, buff, count); -} - -/* - * clone the current process and runs the specified function - * return 0 if seccess else err code - */ -s32 BBOX_CloneRun(u32 uFlags, s32 (*pFn)(void*), void* pArg, ...) -{ - /* reserve 4K when calling and running a function to protect waitpid can exit correct. */ - /* CLONE_UNTRACED flag is must or gdb will debug new process which is cloned. */ - pid_t pid; - if (pArg == NULL || pFn == NULL) { - return -1; - } - - pid = sys__clone(pFn, (((char*)(pArg)) - 4096), uFlags | CLONE_UNTRACED, pArg, 0, 0, 0); - - return pid; -} - -/* -function name: BBOX_GetTaskNumber -description: When get a path to specific process, this function will return count of threads below it. -arguments: A pointer of type char*, including a path to specific process. -return value: An integer that indicates the count of threads below specific process. -note: none -date: 2022/8/3 -contact tel: 18720816902 -*/ -s32 BBOX_GetTaskNumber(char* szTaskPath) -{ - struct kernel_stat stProcSB = {0}; - s32 iProc = -1; - - if (szTaskPath == NULL) { - bbox_print(PRINT_ERR, "parameter szTaskPath is null.\n"); - - return -1; - } - - /* open /proc */ - iProc = sys_open(szTaskPath, O_RDONLY | O_DIRECTORY, 0); - if (iProc < 0) { - bbox_print(PRINT_ERR, "open file %s failed, errno = %d\n", szTaskPath, errno); - - return -1; - } - - /* get the number of first file nodes in the directory */ - if (sys_fstat(iProc, &stProcSB)) { - bbox_print(PRINT_ERR, "Get file %s fstat, errno = %d, iProc = %d\n", szTaskPath, errno, iProc); - - sys_close(iProc); - return -1; - } - - sys_close(iProc); - - return stProcSB.st_nlink; -} - -/* -function name: BBOX_GetTaskId -description: When get a path to specific process, this function will return count of threads below it. -arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*, - we use it as a structure array to store requisite thread infomation, the next argument destines - the max size of the array that the first argument destines. The last argument is a pointer of type - char*, including a path to specific process. -return value: An integer that indicates the count of threads stored in structure array. -note: none -date: 2022/8/3 -contact tel: 18720816902 -*/ -s32 BBOX_GetTaskId(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iSize, char* szTaskPath) -{ - s32 iProc = -1; - pid_t pid = 0; - struct linux_dirent* pstEntry = NULL; - char szBuff[PAGE_SIZE]; - ssize_t nBytes; - const char* pszPtr = NULL; - s32 iThreadCount = 0; - - if (pstTaskInfo == NULL || szTaskPath == NULL) { - bbox_print(PRINT_ERR, - "parameter is invalid, pstTaskInfo or szTaskPath is NULL, iSize = %d \n", - iSize); - - return -1; - } - - iProc = sys_open(szTaskPath, O_RDONLY | O_DIRECTORY, 0); - if (iProc < 0) { - bbox_print(PRINT_ERR, "open '%s' failed, errno = %d\n", szTaskPath, errno); - - return -1; - } - - /* traverse /proc/[pid]/task, get count of thread and pid. */ - for (; iThreadCount < iSize;) { - /* get pointer to the directory structure */ - nBytes = sys_getdents(iProc, (struct linux_dirent*)szBuff, sizeof(szBuff)); - if (nBytes < 0) { - bbox_print(PRINT_ERR, "Get dents '%s' failed, errno = %d\n", szTaskPath, errno); - - goto errout; - } else if (0 == nBytes) { - sys_lseek(iProc, 0, SEEK_SET); - break; - } - - /* recursively traversing the directory */ - for (pstEntry = (struct linux_dirent*)szBuff; - (pstEntry < (struct linux_dirent*)&szBuff[nBytes]) && (iThreadCount < iSize); - pstEntry = (struct linux_dirent*)((char*)pstEntry + pstEntry->d_reclen)) { - if (pstEntry->d_ino == 0) { - continue; - } - - pszPtr = pstEntry->d_name; - bbox_print(PRINT_DBG, "dir: %s/%s\n", szTaskPath, pszPtr); - - if (*pszPtr == '.') { - pszPtr++; - } - - /* ignore directory that is not a proc. */ - if (*pszPtr < '0' || *pszPtr > '9') { - continue; - } - - /* get pid information */ - pid = bbox_atoi(pszPtr); - - if (!pid || pid == sys_gettid()) { - bbox_print(PRINT_ERR, "pid is invalid , pid = %d\n", pid); - - continue; - } - - /* add thread information into a array. */ - pstTaskInfo[iThreadCount].pid = pid; - pstTaskInfo[iThreadCount].cIsAttached = 0; - iThreadCount++; - } - } - - sys_close(iProc); - return iThreadCount; -errout: - sys_close(iProc); - return -1; -} - -/* -function name: BBOX_PtraceAttachPid -description: The function is used to check the process whose id stored in structure array pstTaskInfo work normally. -arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*, - it is used as a structure array that has stored requisite thread infomation, the next argument destines - the size of the array that the first argument destines, namely how many elements the array has. - The last argument is an integer to decide if need to check if the trace to destined process - work normally, if normal, corresponding element of array pstTaskInfo's member variable cIsAttached - will change from 0 to 1. -return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR. -note: none -date: 2022/8/3 -contact tel: 18720816902 -*/ -s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s32 iDoPtraceCheck) -{ - u32 i; - pid_t pid; - unsigned long m = 0; - unsigned long n = 0; - - if (pstTaskInfo == NULL || iPidCount <= 0) { - return RET_ERR; - } - - for (i = 0; i < (u32)iPidCount; i++) { - /* walk through all the threads and debug the trace. */ - pid = pstTaskInfo[i].pid; - if (sys_ptrace(PTRACE_ATTACH, pid, (void*)0, (void*)0) < 0) { - bbox_print(PRINT_ERR, "ptrace failed, pid = %d, errno = %d\n", pid, errno); - continue; - } - - /* check that if the thread state is normal. */ - while (sys_waitpid(pid, (int*)0, __WALL) < 0) { - if (errno != EINTR) { - bbox_print(PRINT_ERR, "wait for %d failed, errno = %d\n", pid, errno); - - sys_ptrace(PTRACE_DETACH, pid, 0, 0); - break; - } - } - - if (iDoPtraceCheck) { - int ret = 0; - ret = sys_ptrace(PTRACE_PEEKDATA, pid, &m, &n); - /* check that if the trace is valid */ - if (ret || (m != n)) { - bbox_print(PRINT_ERR, - "ptrace peek data failed, pid = %d, ret = %d,m = %lu, n = %lu, errno = %d\n", - pid, ret, m, n, errno); - - sys_ptrace(PTRACE_DETACH, pid, 0, 0); - continue; - } - } - - bbox_print(PRINT_LOG, "ptrace attach %d\n", pid); - pstTaskInfo[i].cIsAttached = 1; - } - - return RET_OK; -} - -/* -function name: BBOX_DetachAllThread -description: The function is used to cancel checking the process whose id stored in structure array pstTaskInfo - work normally, "work normally" means in array pstTaskInfo corresponding element's member - variable cIsAttached's value is 1. -arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*, - it is used as a structure array that has stored requisite thread infomation, the next argument destines - the size of the array that the first argument destines, namely how many elements the array has. -return value: void -note: none -date: 2022/8/3 -contact tel: 18720816902 -*/ -void BBOX_DetachAllThread(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount) -{ - u32 i; - pid_t pid; - - if (pstTaskInfo == NULL || iPidCount <= 0) { - return; - } - - for (i = 0; i < (u32)iPidCount; i++) { - pid = pstTaskInfo[i].pid; - if (!pstTaskInfo[i].cIsAttached) { - continue; - } - bbox_print(PRINT_LOG, "ptrace detach %d\n", pid); - - /* cancel track. */ - sys_sched_yield(); - sys_ptrace(PTRACE_DETACH, pid, 0, 0); - sys_kill(pid, SIGCONT); - } - - return; -} - -/* - * notification function after dump module have got all data, to recover a thread. - */ -void BBOX_CheckResumeThread(void* pArgs) -{ - struct TASK_CHECK_RESUME_ARGS* pstTaskCheck = NULL; - - if (pArgs == NULL) { - return; - } - - pstTaskCheck = (struct TASK_CHECK_RESUME_ARGS*)pArgs; - - if (GET_TYPE_SNAP == pstTaskCheck->enType) { - BBOX_DetachAllThread(pstTaskCheck->pstTaskInfo, pstTaskCheck->iThreadCount); - } -} - -/* -function name: BBOX_PtraceAndRun -description: When get a path to specific process, this function will trace the threads below it, and get the - information for example how many threads work normally then store it in pstArgs. -arguments: The first argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*, - what matters is its member variable callback function pointer, the next argument destines - the max count of the thread. The last argument is a pointer of type char*, including a path - to specific process. -return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR. -note: none -date: 2022/8/3 -contact tel: 18720816902 -*/ -s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char* pszProcSelfTask) -{ - struct TASK_ATTACH_INFO stTaskInfo[iMaxThreadCount]; - pid_t thread_pids[iMaxThreadCount]; - - struct TASK_CHECK_RESUME_ARGS stTaskCheck; - s32 iThreadCount = 0; - s32 iAttachCount = 0; - s32 iRet = 0; - s32 iDoPtraceCheck = 1; - s32 i; - - if (pstArgs == NULL || pszProcSelfTask == NULL || iMaxThreadCount <= 0) { - - bbox_print(PRINT_ERR, - "Parameter is invald, pstArgs or pszProcSelfTask is NULL, iMaxThreadCount = %d \n", - iMaxThreadCount); - - return RET_ERR; - } - - errno_t rc = memset_s(stTaskInfo, sizeof(stTaskInfo), 0, sizeof(stTaskInfo)); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(thread_pids, sizeof(thread_pids), 0, sizeof(thread_pids)); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(&stTaskCheck, sizeof(stTaskCheck), 0, sizeof(stTaskCheck)); - securec_check_c(rc, "\0", "\0"); - - iThreadCount = BBOX_GetTaskId(stTaskInfo, iMaxThreadCount, pszProcSelfTask); - if (iThreadCount <= 0) { - bbox_print(PRINT_ERR, "Get task id failed.\n"); - - goto errout; - } - - if (GET_TYPE_DUMP != pstArgs->enGetType) { - iDoPtraceCheck = 0; - } else { - iDoPtraceCheck = 1; - } - - iRet = BBOX_PtraceAttachPid(stTaskInfo, iThreadCount, iDoPtraceCheck); - if (iRet != RET_OK) { - bbox_print(PRINT_ERR, "Ptrace attache failed.\n"); - goto errout; - } - - /* copy information of thread that have been attaching. */ - for (i = 0; i < iThreadCount; i++) { - if (!stTaskInfo[i].cIsAttached) { - continue; - } - thread_pids[iAttachCount] = stTaskInfo[i].pid; - iAttachCount++; - } - - stTaskCheck.pstTaskInfo = stTaskInfo; - stTaskCheck.enType = pstArgs->enGetType; - stTaskCheck.iThreadCount = iThreadCount; - - /* run call back function. */ - bbox_print(PRINT_TIP, "Thread count :%d\n", iThreadCount); - bbox_print(PRINT_TIP, "Ptraced thread count :%d\n", iAttachCount); - bbox_print(PRINT_LOG, "Run callback: thread count = %d\n", iThreadCount); - /* Callback to the thread information handler. */ - pstArgs->iResult = pstArgs->pCallBack(BBOX_CheckResumeThread, &stTaskCheck, iAttachCount, thread_pids, pstArgs->ap); - pstArgs->iError = errno; - - BBOX_DetachAllThread(stTaskInfo, iThreadCount); - - return RET_OK; - -errout: - BBOX_DetachAllThread(stTaskInfo, iThreadCount); - return RET_ERR; -} - -/* -function name: BBOX_PrintFailedLog -description: Write log infomation into specific file, if errors arise, print the infomation about errors. -arguments: The only argument is a pointer of type const char* to a filename string, if this file doesn't - exist, we will creat a new file named it. -return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR. -note: none -date: 2022/8/3 -contact tel: 18720816902 -*/ -void BBOX_PrintFailedLog(const char* pFileName) -{ - ssize_t iRet = 0; - s32 iWriteSize = 0; - s32 iBboxLogFd = -1; - - iBboxLogFd = sys_open(pFileName, O_RDWR | O_CREAT | O_TRUNC, 0600); - if (iBboxLogFd < 0) { - - bbox_print(PRINT_ERR, "open failed, errno = %d\n", errno); - - return; - } - - iWriteSize = bbox_strnlen(g_acBBoxLog, BBOX_LOG_SIZE) + 1; - iWriteSize = (iWriteSize > BBOX_LOG_SIZE) ? BBOX_LOG_SIZE : iWriteSize; - iRet = sys_write(iBboxLogFd, g_acBBoxLog, iWriteSize); - if (iRet < 0) { - bbox_print(PRINT_ERR, "write failed, errno = %d\n", errno); - - sys_close(iBboxLogFd); - return; - } - - sys_close(iBboxLogFd); -} - -/* -function name: BBOX_ListThread -description: Export thread information. -arguments: The only argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*, - what matters is its member variable callback function pointer and thread infomation. -return value: void -note: none -date: 2022/8/3 -contact tel: 18720816902 -*/ -void BBOX_ListThread(struct BBOX_ListParams* pstArgs) -{ - pid_t ppid = 0; - s32 iMaker = -1; - s32 iMaxThreadCount = 0; - s32 iRet = 0; - - struct kernel_stat stMarkerSB; - char szProcSelfTask[BBOX_PROC_PATH_LEN]; - char pszMarkPath[BBOX_PROC_PATH_LEN]; - stack_t altstack; - errno_t rc = EOK; - - if (pstArgs == NULL) { - bbox_print(PRINT_ERR, "pstArgs is NULL.\n"); - - return; - } - - ppid = sys_getppid(); - - iMaker = sys_socket(PF_LOCAL, SOCK_DGRAM, 0); - if (iMaker < 0) { - bbox_print(PRINT_ERR, "sys_socket error, errno = %d\n", errno); - goto errout; - } - - if (sys_fcntl(iMaker, F_SETFD, FD_CLOEXEC) < 0) { - bbox_print(PRINT_ERR, "sys_fcntl error, errno = %d\n", errno); - goto errout; - } - - if (bbox_snprintf(szProcSelfTask, BBOX_PROC_PATH_LEN, "/proc/%d/task", ppid) <= 0) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - goto errout; - } - - if (bbox_snprintf(pszMarkPath, BBOX_PROC_PATH_LEN, "/proc/%d/fd/%d", ppid, iMaker) <= 0) { - bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); - goto errout; - } - - bbox_print(PRINT_TIP, "Get information for pid %d:\n", ppid); - - rc = memset_s(&stMarkerSB, sizeof(stMarkerSB), 0, sizeof(stMarkerSB)); - securec_check_c(rc, "\0", "\0"); - - if (sys_stat(pszMarkPath, &stMarkerSB) < 0) { - bbox_print(PRINT_ERR, "sys_stat error, errno = %d, path = %s\n", errno, pszMarkPath); - goto errout; - } - - /* switch stack pointer */ - rc = memset_s(&altstack, sizeof(altstack), 0, sizeof(altstack)); - securec_check_c(rc, "\0", "\0"); - altstack.ss_sp = pstArgs->pAltStackMem; - altstack.ss_flags = 0; - altstack.ss_size = BBOX_ALT_STACKSIZE; - sys_sigaltstack(&altstack, (const stack_t*)NULL); - - /* get max count of task. */ - iMaxThreadCount = BBOX_GetTaskNumber(szProcSelfTask); - if (iMaxThreadCount <= 0) { - bbox_print(PRINT_ERR, "Get task number failed.\n"); - goto errout; - } - - /* ptrace and run thread. */ - iRet = BBOX_PtraceAndRun(pstArgs, iMaxThreadCount, szProcSelfTask); - if (iRet != RET_OK) { - bbox_print(PRINT_ERR, "ptrace task and run failed.\n"); - goto errout; - } - - bbox_print(PRINT_TIP, "Get information success.\n"); - - sys_close(iMaker); - - if (RET_OK != pstArgs->iResult) { - BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2)); - } - - if (pstArgs->pDoneCallback != NULL) { - pstArgs->pDoneCallback(pstArgs->pDoneArgs); - } - - sys_exit(0); -errout: - if (iMaker > 0) { - sys_close(iMaker); - } - - bbox_print(PRINT_ERR, "Get information failed.\n"); - - pstArgs->iResult = -1; - pstArgs->iError = errno; - - BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2)); - if (pstArgs->pDoneCallback != NULL) { - pstArgs->pDoneCallback(pstArgs->pDoneArgs); - } - - sys_exit(1); -} - -/* -function name: BBOX_GetClonePidResult -description: The function get the status of child process at first, then according to it assign pstArgs's - member variables iError and iResult appropriate values. -arguments: The first argument is a integer named iClonePid, it represents the pid of child process. - The second argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*, - what matters is its member variable callback function pointer and thread infomation. - The third argument is a integer indicating error code. -return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR. -note: none -date: 2022/8/3 -contact tel: 18720816902 -*/ -s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 iCloneErrno) -{ - s32 iStatus = 0; - s32 iRet = 0; - - if (iClonePid < 0) { - - bbox_print(PRINT_ERR, "Clone failed, can't create child process, errno = %d.\n", iCloneErrno); - - BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2)); - if (pstArgs->pDoneCallback != NULL) { - pstArgs->pDoneCallback(pstArgs->pDoneArgs); - } - - return RET_ERR; - } - - /* wait child process exit. */ - while ((iRet = sys_waitpid(iClonePid, &iStatus, __WALL)) < 0 && errno == EINTR) { - continue; - } - - bbox_print(PRINT_LOG, "clone pid %d ret is %x, status = %x\n", iClonePid, iRet, WIFEXITED(iStatus)); - - if (iRet < 0) { - pstArgs->iError = errno; - pstArgs->iResult = -1; - } else if (WIFEXITED(iStatus)) { - switch (WEXITSTATUS(iStatus)) { - case 0: - break; - case 2: - pstArgs->iError = EFAULT; - pstArgs->iResult = -1; - break; - case 3: - pstArgs->iError = EPERM; - pstArgs->iResult = 1; - break; - default: - pstArgs->iError = ECHILD; - pstArgs->iResult = -1; - break; - } - } else if (!WIFEXITED(iStatus)) { - pstArgs->iError = EFAULT; - pstArgs->iResult = -1; - bbox_print(PRINT_ERR, "WIFEXITED status failed"); - } else { - pstArgs->iError = iCloneErrno; - pstArgs->iResult = -1; - bbox_print(PRINT_ERR, "WIFEXITED error, errno = %d\n", iCloneErrno); - } - - return RET_OK; -} - -/* - * get all threads and run specify function - */ -s32 BBOX_GetAllThreads( - GET_THREAD_TYPE enType, BBOX_GetAllThreadDone pDone, void* pDoneArgs, BBOX_GetAllThreadsCallBack pCallback, ...) -{ - struct BBOX_ListParams stArgs; - struct kernel_sigset_t stSigBlocked; - struct kernel_sigset_t stSigOld; - s32 iDumpable = 1; - s32 iSigNo; - s32 iCloneErrno; - pid_t ClonePid; - - errno = 0; - - if (BBOX_AtomicIncReturn(&g_isBusy) > 1) { - BBOX_AtomicDec(&g_isBusy); - - bbox_print(PRINT_ERR, "Dump task is running.\n"); - - errno = EALREADY; - return -1; - } - - if (enType >= GET_TYPE_BUTT || pCallback == NULL) { - bbox_print(PRINT_ERR, "Parameter is invalid, enType = %d, and maybe pCallback is NULL", enType); - BBOX_AtomicDec(&g_isBusy); - return -1; - } - - errno_t rc = memset_s(&stArgs, sizeof(stArgs), 0, sizeof(stArgs)); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(&stSigBlocked, sizeof(stSigBlocked), 0, sizeof(stSigBlocked)); - securec_check_c(rc, "\0", "\0"); - rc = memset_s(&stSigOld, sizeof(stSigOld), 0, sizeof(stSigOld)); - securec_check_c(rc, "\0", "\0"); - - va_start(stArgs.ap, pCallback); - - /* clear new stack */ - rc = memset_s(g_szAltStackMem, BBOX_ALT_STACKSIZE, 0, BBOX_ALT_STACKSIZE); - securec_check_c(rc, "\0", "\0"); - /* reserve 32K */ - BBOX_ReserveZeroStack(1024 * 32); - - /* check and set dump flag. */ - iDumpable = sys_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); - if (!iDumpable) { - sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); - } - - /* set start parameter of dump thread. */ - stArgs.iResult = -1; - stArgs.iError = 0; - stArgs.pAltStackMem = g_szAltStackMem; - stArgs.pCallBack = pCallback; - stArgs.pDoneCallback = pDone; - stArgs.pDoneArgs = pDoneArgs; - stArgs.enGetType = enType; - - /* suspend all signals */ - sys_sigfillset(&stSigBlocked); - for (iSigNo = 0; iSigNo < (s32)(sizeof(iSyncSignals) / sizeof(*iSyncSignals)); iSigNo++) { - sys_sigdelset(&stSigBlocked, iSyncSignals[iSigNo]); - } - - /* block all signals */ - if (sys_sigprocmask(SIG_BLOCK, &stSigBlocked, &stSigOld)) { - stArgs.iError = errno; - stArgs.iResult = -1; - bbox_print(PRINT_ERR, "sys_sigprocmask error, errno = %d\n", errno); - goto errout; - } - - /* create child process and run function to export thread information. */ - if (GET_TYPE_DUMP == enType) { - - ClonePid = BBOX_CloneRun(CLONE_VM | CLONE_FS | CLONE_FILES, (s32(*)(void*))BBOX_ListThread, &stArgs); - } else { - /* copy VMA if type is snapshoot. */ - ClonePid = BBOX_CloneRun(CLONE_FS | CLONE_FILES, (s32(*)(void*))BBOX_ListThread, &stArgs); - } - - iCloneErrno = errno; - - /* restoring signal */ - sys_sigprocmask(SIG_SETMASK, &stSigOld, &stSigOld); - - if (BBOX_GetClonePidResult(ClonePid, &stArgs, iCloneErrno) != RET_OK) { - bbox_print(PRINT_ERR, "BBOX_GetClonePidResult error\n"); - } - -errout: - BBOX_AtomicDec(&g_isBusy); - - if (!iDumpable) { - sys_prctl(PR_SET_DUMPABLE, iDumpable, 0, 0, 0); - } - - va_end(stArgs.ap); - - errno = stArgs.iError; - return stArgs.iResult; -} -- 2.34.1 From a64a9be96d2dd477e34fa597826df38f4f926042 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 21:01:04 +0800 Subject: [PATCH 31/56] ADD file via upload --- src/gausskernel/cbb/bbox/bbox_threads.cpp | 868 ++++++++++++++++++++++ 1 file changed, 868 insertions(+) create mode 100644 src/gausskernel/cbb/bbox/bbox_threads.cpp diff --git a/src/gausskernel/cbb/bbox/bbox_threads.cpp b/src/gausskernel/cbb/bbox/bbox_threads.cpp new file mode 100644 index 000000000..b7a677a06 --- /dev/null +++ b/src/gausskernel/cbb/bbox/bbox_threads.cpp @@ -0,0 +1,868 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * bbox_threads.cpp + * + * IDENTIFICATION + * src/gausskernel/cbb/bbox/bbox_threads.cpp + * + * ------------------------------------------------------------------------- + */ +#include "bbox_syscall_support.h" +#include "bbox_threads.h" +#include "bbox_print.h" +#include "bbox_atomic.h" +#include "../../src/include/securec.h" +#include "../../src/include/securec_check.h" + +static const u32 iSyncSignals[] = { + SIGABRT, + SIGILL, + SIGFPE, + SIGSEGV, + SIGBUS, + SIGXCPU, + SIGXFSZ, +}; + +struct TASK_ATTACH_INFO { + pid_t pid; /* pid */ + u8 cIsAttached; /* Whether the thread is ptraced */ +}; + +struct TASK_CHECK_RESUME_ARGS { + struct TASK_ATTACH_INFO* pstTaskInfo; /* recover process information */ + s32 iThreadCount; /* thread count */ + GET_THREAD_TYPE enType; /* get thread type */ +}; + +struct BBOX_ListParams { + s32 iResult; /* result */ + s32 iError; /* error code */ + u8* pAltStackMem; /* Execution stack information */ + BBOX_GetAllThreadsCallBack pCallBack; /* Executes the callback function exported by the thread */ + BBOX_GetAllThreadDone pDoneCallback; /* call back function after exporting the thread information */ + void* pDoneArgs; /* parameter of call back function pDoneCallback */ + GET_THREAD_TYPE enGetType; /* get data type */ + va_list ap; /* parameter list of function pCallBack */ +}; + +u8 g_szAltStackMem[BBOX_ALT_STACKSIZE]; /* independent thread stack memory */ + +BBOX_ATOMIC_STRU g_isBusy = BBOX_ATOMIC_INIT(0); /* whether deal with core file. */ + +/* +function name: BBOX_ReserveZeroStack +description: The function creat a empty stack, and its size depend on argument count. +arguments: An integer of type s32, namely int, it destines the storage of stack. +return value: void +note: The stack this function creats is actually a character array. +date: 2022/8/3 +contact tel: 18720816902 +*/ + +/* +void BBOX_ReserveZeroStack(s32 count)函数是一个预留并清零堆栈的函数。具体实现如下: + +1. 创建一个大小为count的字符数组buff。 +2. 调用memset_s函数将buff的值全部设置为0。 +3. 调用sys_read函数将buff的值从文件描述符-1读取进来。实际上此处读取的操作是无效的,只是为了预留并使用堆栈空间。 +*/ +void BBOX_ReserveZeroStack(s32 count) +{ + char buff[count]; + + errno_t rc = memset_s(buff, count, 0, count); + securec_check_c(rc, "\0", "\0"); + + (void)sys_read(-1, buff, count); +} + +/* + * clone the current process and runs the specified function + * return 0 if seccess else err code + */ + /* + s32 BBOX_CloneRun(u32 uFlags, s32 (*pFn)(void*), void* pArg, ...)函数是一个克隆当前进程并运行指定函数的函数。具体实现如下: + +1. 首先判断pArg和pFn是否为NULL,如果为NULL则返回-1。 +2. 调用sys__clone函数克隆当前进程,并将指定的函数和参数传递给新创建的进程。 +3. 返回新创建进程的pid。 +*/ +s32 BBOX_CloneRun(u32 uFlags, s32 (*pFn)(void*), void* pArg, ...) +{ + /* reserve 4K when calling and running a function to protect waitpid can exit correct. */ + /* CLONE_UNTRACED flag is must or gdb will debug new process which is cloned. */ + pid_t pid; + if (pArg == NULL || pFn == NULL) { + return -1; + } + + pid = sys__clone(pFn, (((char*)(pArg)) - 4096), uFlags | CLONE_UNTRACED, pArg, 0, 0, 0); + + return pid; +} + +/* +function name: BBOX_GetTaskNumber +description: When get a path to specific process, this function will return count of threads below it. +arguments: A pointer of type char*, including a path to specific process. +return value: An integer that indicates the count of threads below specific process. +note: none +date: 2022/8/3 +contact tel: 18720816902 +*/ +s32 BBOX_GetTaskNumber(char* szTaskPath) +{ + struct kernel_stat stProcSB = {0}; + s32 iProc = -1; + + if (szTaskPath == NULL) { + bbox_print(PRINT_ERR, "parameter szTaskPath is null.\n"); + + return -1; + } + + /* open /proc */ + iProc = sys_open(szTaskPath, O_RDONLY | O_DIRECTORY, 0); + if (iProc < 0) { + bbox_print(PRINT_ERR, "open file %s failed, errno = %d\n", szTaskPath, errno); + + return -1; + } + + /* get the number of first file nodes in the directory */ + if (sys_fstat(iProc, &stProcSB)) { + bbox_print(PRINT_ERR, "Get file %s fstat, errno = %d, iProc = %d\n", szTaskPath, errno, iProc); + + sys_close(iProc); + return -1; + } + + sys_close(iProc); + + return stProcSB.st_nlink; +} + +/* +function name: BBOX_GetTaskId +description: When get a path to specific process, this function will return count of threads below it. +arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*, + we use it as a structure array to store requisite thread infomation, the next argument destines + the max size of the array that the first argument destines. The last argument is a pointer of type + char*, including a path to specific process. +return value: An integer that indicates the count of threads stored in structure array. +note: none +date: 2022/8/3 +contact tel: 18720816902 +*/ +/* +这是一个用于获取指定路径下进程和线程ID的函数。 + +函数的参数包括指向TASK_ATTACH_INFO结构体的指针pstTaskInfo,整型变量iSize和指向字符数组的指针szTaskPath。 + +函数的主要步骤如下: + +1. 首先检查指针pstTaskInfo和szTaskPath是否为NULL,如果是则输出错误信息并返回-1。 +2. 使用sys_open函数以只读和目录模式打开指定的路径szTaskPath,得到的文件描述符保存在变量iProc中。 +3. 如果iProc小于0,则输出错误信息并返回-1。 +4. 使用循环遍历/proc/[pid]/task目录下的所有文件,其中iThreadCount表示已经获取的进程和线程ID的数量。 +5. 使用sys_getdents函数读取目录项,读取的结果保存在szBuff中,返回的字节数保存在nBytes中。 +6. 如果nBytes小于0,则输出错误信息并跳转到errout标签。 +7. 如果nBytes等于0,则使用sys_lseek函数将文件指针设置到目录的开头位置,然后跳出循环。 +8. 遍历目录项,判断当前项是否为进程或线程的目录。 +9. 提取目录名中的数字作为pid,并将pid存储到pstTaskInfo数组对应的元素中。 +10. 将iThreadCount加1,表示已经获取的进程和线程ID的数量。 +11. 使用sys_close函数关闭iProc。 +12. 返回成功获取的进程和线程ID的数量iThreadCount,如果出错则返回-1。 +*/ +s32 BBOX_GetTaskId(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iSize, char* szTaskPath) +{ + s32 iProc = -1; + pid_t pid = 0; + struct linux_dirent* pstEntry = NULL; + char szBuff[PAGE_SIZE]; + ssize_t nBytes; + const char* pszPtr = NULL; + s32 iThreadCount = 0; + + if (pstTaskInfo == NULL || szTaskPath == NULL) { + bbox_print(PRINT_ERR, + "parameter is invalid, pstTaskInfo or szTaskPath is NULL, iSize = %d \n", + iSize); + + return -1; + } + + iProc = sys_open(szTaskPath, O_RDONLY | O_DIRECTORY, 0); + if (iProc < 0) { + bbox_print(PRINT_ERR, "open '%s' failed, errno = %d\n", szTaskPath, errno); + + return -1; + } + + /* traverse /proc/[pid]/task, get count of thread and pid. */ + for (; iThreadCount < iSize;) { + /* get pointer to the directory structure */ + nBytes = sys_getdents(iProc, (struct linux_dirent*)szBuff, sizeof(szBuff)); + if (nBytes < 0) { + bbox_print(PRINT_ERR, "Get dents '%s' failed, errno = %d\n", szTaskPath, errno); + + goto errout; + } else if (0 == nBytes) { + sys_lseek(iProc, 0, SEEK_SET); + break; + } + + /* recursively traversing the directory */ + for (pstEntry = (struct linux_dirent*)szBuff; + (pstEntry < (struct linux_dirent*)&szBuff[nBytes]) && (iThreadCount < iSize); + pstEntry = (struct linux_dirent*)((char*)pstEntry + pstEntry->d_reclen)) { + if (pstEntry->d_ino == 0) { + continue; + } + + pszPtr = pstEntry->d_name; + bbox_print(PRINT_DBG, "dir: %s/%s\n", szTaskPath, pszPtr); + + if (*pszPtr == '.') { + pszPtr++; + } + + /* ignore directory that is not a proc. */ + if (*pszPtr < '0' || *pszPtr > '9') { + continue; + } + + /* get pid information */ + pid = bbox_atoi(pszPtr); + + if (!pid || pid == sys_gettid()) { + bbox_print(PRINT_ERR, "pid is invalid , pid = %d\n", pid); + + continue; + } + + /* add thread information into a array. */ + pstTaskInfo[iThreadCount].pid = pid; + pstTaskInfo[iThreadCount].cIsAttached = 0; + iThreadCount++; + } + } + + sys_close(iProc); + return iThreadCount; +errout: + sys_close(iProc); + return -1; +} + +/* +function name: BBOX_PtraceAttachPid +description: The function is used to check the process whose id stored in structure array pstTaskInfo work normally. +arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*, + it is used as a structure array that has stored requisite thread infomation, the next argument destines + the size of the array that the first argument destines, namely how many elements the array has. + The last argument is an integer to decide if need to check if the trace to destined process + work normally, if normal, corresponding element of array pstTaskInfo's member variable cIsAttached + will change from 0 to 1. +return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR. +note: none +date: 2022/8/3 +contact tel: 18720816902 +*/ +/* +s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s32 iDoPtraceCheck)函数是用于检查指定进程的跟踪状态是否正常的函数。 + +首先检查pstTaskInfo和iPidCount是否合法,如果不合法则返回指定的错误码。 +使用循环遍历pstTaskInfo数组中的每个任务。 +使用sys_ptrace函数将指定任务的跟踪状态设置为跟踪状态。 +使用sys_waitpid函数等待指定任务结束。 +如果等待失败并且错误码不是EINTR,则输出错误信息,然后使用sys_ptrace函数将跟踪状态取消。 +如果iDoPtraceCheck为真,则使用sys_ptrace函数检查跟踪状态是否有效。 +如果检查失败,输出错误信息并取消跟踪状态。 +将任务的cIsAttached成员变量设置为已跟踪状态。 +返回正常执行的结果码。 +*/ +s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s32 iDoPtraceCheck) +{ + u32 i; + pid_t pid; + unsigned long m = 0; + unsigned long n = 0; + + if (pstTaskInfo == NULL || iPidCount <= 0) { + return RET_ERR; + } + + for (i = 0; i < (u32)iPidCount; i++) { + /* walk through all the threads and debug the trace. */ + pid = pstTaskInfo[i].pid; + if (sys_ptrace(PTRACE_ATTACH, pid, (void*)0, (void*)0) < 0) { + bbox_print(PRINT_ERR, "ptrace failed, pid = %d, errno = %d\n", pid, errno); + continue; + } + + /* check that if the thread state is normal. */ + while (sys_waitpid(pid, (int*)0, __WALL) < 0) { + if (errno != EINTR) { + bbox_print(PRINT_ERR, "wait for %d failed, errno = %d\n", pid, errno); + + sys_ptrace(PTRACE_DETACH, pid, 0, 0); + break; + } + } + + if (iDoPtraceCheck) { + int ret = 0; + ret = sys_ptrace(PTRACE_PEEKDATA, pid, &m, &n); + /* check that if the trace is valid */ + if (ret || (m != n)) { + bbox_print(PRINT_ERR, + "ptrace peek data failed, pid = %d, ret = %d,m = %lu, n = %lu, errno = %d\n", + pid, ret, m, n, errno); + + sys_ptrace(PTRACE_DETACH, pid, 0, 0); + continue; + } + } + + bbox_print(PRINT_LOG, "ptrace attach %d\n", pid); + pstTaskInfo[i].cIsAttached = 1; + } + + return RET_OK; +} + +/* +function name: BBOX_DetachAllThread +description: The function is used to cancel checking the process whose id stored in structure array pstTaskInfo + work normally, "work normally" means in array pstTaskInfo corresponding element's member + variable cIsAttached's value is 1. +arguments: The first argument is a structure pointer named pstTaskInfo,its type is struct TASK_ATTACH_INFO*, + it is used as a structure array that has stored requisite thread infomation, the next argument destines + the size of the array that the first argument destines, namely how many elements the array has. +return value: void +note: none +date: 2022/8/3 +contact tel: 18720816902 +*/ +void BBOX_DetachAllThread(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount) +{ + u32 i; + pid_t pid; + + if (pstTaskInfo == NULL || iPidCount <= 0) { + return; + } + + for (i = 0; i < (u32)iPidCount; i++) { + pid = pstTaskInfo[i].pid; + if (!pstTaskInfo[i].cIsAttached) { + continue; + } + bbox_print(PRINT_LOG, "ptrace detach %d\n", pid); + + /* cancel track. */ + sys_sched_yield(); + sys_ptrace(PTRACE_DETACH, pid, 0, 0); + sys_kill(pid, SIGCONT); + } + + return; +} + +/* + * notification function after dump module have got all data, to recover a thread. + */ +void BBOX_CheckResumeThread(void* pArgs) +{ + struct TASK_CHECK_RESUME_ARGS* pstTaskCheck = NULL; + + if (pArgs == NULL) { + return; + } + + pstTaskCheck = (struct TASK_CHECK_RESUME_ARGS*)pArgs; + + if (GET_TYPE_SNAP == pstTaskCheck->enType) { + BBOX_DetachAllThread(pstTaskCheck->pstTaskInfo, pstTaskCheck->iThreadCount); + } +} + +/* +function name: BBOX_PtraceAndRun +description: When get a path to specific process, this function will trace the threads below it, and get the + information for example how many threads work normally then store it in pstArgs. +arguments: The first argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*, + what matters is its member variable callback function pointer, the next argument destines + the max count of the thread. The last argument is a pointer of type char*, including a path + to specific process. +return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR. +note: none +date: 2022/8/3 +contact tel: 18720816902 +*/ +/* +这段代码是一个函数,接受一个指向结构体BBOX_ListParams的指针pstArgs、一个最大线程数量iMaxThreadCount和一个指向char类型的指针pszProcSelfTask作为参数。 +代码的作用是使用ptrace操作来追踪并运行线程。 +*/ +s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char* pszProcSelfTask) +{ + // 声明一些变量 + struct TASK_ATTACH_INFO stTaskInfo[iMaxThreadCount]; // 存储线程信息的数组 + pid_t thread_pids[iMaxThreadCount]; // 存储线程PID的数组 + + struct TASK_CHECK_RESUME_ARGS stTaskCheck; // 追踪线程所需参数 + s32 iThreadCount = 0; // 线程数量 + s32 iAttachCount = 0; // 被追踪的线程数量 + s32 iRet = 0; // 返回值变量 + s32 iDoPtraceCheck = 1; // 是否需要使用ptrace检查 + + s32 i; + + // 参数检查,如果参数无效则打印错误信息并返回错误码 + if (pstArgs == NULL || pszProcSelfTask == NULL || iMax 线程数量小于等于0) { + bbox_print(PRINT_ERR, + "Parameter is invald, pstArgs or pszProcSelfTask is NULL, iMaxThreadCount = %d \n", + iMaxThreadCount); + + return RET_ERR; + } + + // 初始化stTaskInfo、thread_pids和stTaskCheck为0 + errno_t rc = memset_s(stTaskInfo, sizeof(stTaskInfo), 0, sizeof(stTaskInfo)); + securec_check_c(rc, "\0", "\0"); + rc = memset_s(thread_pids, sizeof(thread_pids), 0, sizeof(thread_pids)); + securec_check_c(rc, "\0", "\0"); + rc = memset_s(&stTaskCheck, sizeof(stTaskCheck), 0, sizeof(stTaskCheck)); + securec_check_c(rc, "\0", "\0"); + + // 获取线程数量 + iThreadCount = BBOX_GetTaskId(stTaskInfo, iMaxThreadCount, pszProcSelfTask); + if (iThreadCount <= 0) { + bbox_print(PRINT_ERR, "Get task id failed.\n"); + + goto errout; + } + + // 根据pstArgs的enGetType判断是否需要使用ptrace检查 + if (GET_TYPE_DUMP != pstArgs->enGetType) { + iDoPtraceCheck = 0; + } else { + iDoPtraceCheck = 1; + } + + // 使用BBOX_PtraceAttachPid函数对线程进行ptrace追踪 + iRet = BBOX_PtraceAttachPid(stTaskInfo, iThreadCount, iDoPtraceCheck); + if (iRet != RET_OK) { + bbox_print(PRINT_ERR, "Ptrace attache failed.\n"); + goto errout; + } + + // 将已经被追踪的线程的PID复制到thread_pids数组中 + for (i = 0; i < iThreadCount; i++) { + if (!stTaskInfo[i].cIsAttached) { + continue; + } + thread_pids[iAttachCount] = stTaskInfo[i].pid; + iAttachCount++; + } + + // 设置stTaskCheck的相应参数 + stTaskCheck.pstTaskInfo = stTaskInfo; + stTaskCheck.enType = pstArgs->enGetType; + stTaskCheck.iThreadCount = iThreadCount; + + // 调用回调函数进行处理 + bbox_print(PRINT_TIP, "Thread count :%d\n", iThreadCount); + bbox_print(PRINT_TIP, "Ptraced thread count :%d\n", iAttachCount); + bbox_print(PRINT_LOG, "Run callback: thread count = %d\n", iThreadCount); + // 调用线程信息处理函数的回调函数 + pstArgs->iResult = pstArgs->pCallBack(BBOX_CheckResumeThread, &stTaskCheck, iAttachCount, thread_pids, pstArgs->ap); + pstArgs->iError = errno; + + // 解除所有线程的追踪 + BBOX_DetachAllThread(stTaskInfo, iThreadCount); + + return RET_OK; + +errout: + // 出错情况下也需要解除所有线程的追踪 + BBOX_DetachAllThread(stTaskInfo, iThreadCount); + return RET_ERR; +} + +/* +function name: BBOX_PrintFailedLog +description: Write log infomation into specific file, if errors arise, print the infomation about errors. +arguments: The only argument is a pointer of type const char* to a filename string, if this file doesn't + exist, we will creat a new file named it. +return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR. +note: none +date: 2022/8/3 +contact tel: 18720816902 +*/ +/* +这段代码是一个函数,接受一个指向char类型的指针pFileName作为参数。代码的作用是将全局变量g_acBBoxLog中的内容输出到文件中。 +*/ +// 函数声明:将全局变量g_acBBoxLog中的内容输出到文件中 +void BBOX_PrintFailedLog(const char* pFileName) +{ + ssize_t iRet = 0; // 返回值变量 + s32 iWriteSize = 0; // 写入的数据大小变量 + s32 iBboxLogFd = -1; // 文件描述符变量,初始化为-1 + + // 使用sys_open函数打开文件,以可读写和创建方式打开,文件的访问权限为0600 + iBboxLogFd = sys_open(pFileName, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (iBboxLogFd < 0) { + bbox_print(PRINT_ERR, "open failed, errno = %d\n", errno); // 打印错误信息 + + return; + } + + // 计算要写入的数据大小 + iWriteSize = bbox_strnlen(g_acBBoxLog, BBOX_LOG_SIZE) + 1; + iWriteSize = (iWriteSize > BBOX_LOG_SIZE) ? BBOX_LOG_SIZE : iWriteSize; + + // 使用sys_write函数将g_acBBoxLog中的内容写入到文件中 + iRet = sys_write(iBboxLogFd, g_acBBoxLog, iWriteSize); + if (iRet < 0) { + bbox_print(PRINT_ERR, "write failed, errno = %d\n", errno); // 打印错误信息 + + sys_close(iBboxLogFd); // 关闭文件描述符 + return; + } + + // 使用sys_close函数关闭文件描述符 + sys_close(iBboxLogFd); +} + +/* +function name: BBOX_ListThread +description: Export thread information. +arguments: The only argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*, + what matters is its member variable callback function pointer and thread infomation. +return value: void +note: none +date: 2022/8/3 +contact tel: 18720816902 +*/ +// 函数声明:获取进程的信息并进行一系列操作 +void BBOX_ListThread(struct BBOX_ListParams* pstArgs) +{ + pid_t ppid = 0; // 父进程的进程ID + s32 iMaker = -1; // socket的文件描述符,初始化为-1 + s32 iMaxThreadCount = 0; // 最大线程数,初始化为0 + s32 iRet = 0; // 返回值变量 + + struct kernel_stat stMarkerSB; // 文件状态结构体 + char szProcSelfTask[BBOX_PROC_PATH_LEN]; // 存放进程任务路径的数组 + char pszMarkPath[BBOX_PROC_PATH_LEN]; // 存放标记路径的数组 + stack_t altstack; // 备用信号栈结构体 + errno_t rc = EOK; // 错误号变量,初始化为EOK + + // 如果pstArgs为空指针,则打印错误信息并返回 + if (pstArgs == NULL) { + bbox_print(PRINT_ERR, "pstArgs is NULL.\n"); + + return; + } + + // 获取父进程的进程ID + ppid = sys_getppid(); + + // 创建一个socket,使用本地通信的地址族,数据报套接字类型,协议为0(自动选择协议) + iMaker = sys_socket(PF_LOCAL, SOCK_DGRAM, 0); + if (iMaker < 0) { + bbox_print(PRINT_ERR, "sys_socket error, errno = %d\n", errno); + goto errout; + } + + // 设置socket的关闭执行标记为FD_CLOEXEC,确保在exec族函数调用时关闭socket + if (sys_fcntl(iMaker, F_SETFD, FD_CLOEXEC) < 0) { + bbox_print(PRINT_ERR, "sys_fcntl error, errno = %d\n", errno); + goto errout; + } + + // 使用bbox_snprintf函数将父进程的任务路径写入szProcSelfTask数组中 + if (bbox_snprintf(szProcSelfTask, BBOX_PROC_PATH_LEN, "/proc/%d/task", ppid) <= 0) { + bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + goto errout; + } + + // 使用bbox_snprintf函数将标记路径写入pszMarkPath数组中 + if (bbox_snprintf(pszMarkPath, BBOX_PROC_PATH_LEN, "/proc/%d/fd/%d", ppid, iMaker) <= 0) { + bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno); + goto errout; + } + + // 打印提示信息,显示正在获取pid为ppid的进程的信息 + bbox_print(PRINT_TIP, "Get information for pid %d:\n", ppid); + + // 使用memset_s函数将stMarkerSB结构体清零 + rc = memset_s(&stMarkerSB, sizeof(stMarkerSB), 0, sizeof(stMarkerSB)); + securec_check_c(rc, "\0", "\0"); + + // 使用sys_stat函数获取pszMarkPath对应文件的状态信息并保存到stMarkerSB结构体中 + if (sys_stat(pszMarkPath, &stMarkerSB) < 0) { + bbox_print(PRINT_ERR, "sys_stat error, errno = %d, path = %s\n", errno, pszMarkPath); + goto errout; + } + + // 切换栈指针为备用信号栈 + rc = memset_s(&altstack, sizeof(altstack), 0, sizeof(altstack)); + securec_check_c(rc, "\0", "\0"); + altstack.ss_sp = pstArgs->pAltStackMem; + altstack.ss_flags = 0; + altstack.ss_size = BBOX_ALT_STACKSIZE; + sys_sigaltstack(&altstack, (const stack_t*)NULL); + + // 获取进程的最大线程数 + iMaxThreadCount = BBOX_GetTaskNumber(szProcSelfTask); + if (iMaxThreadCount <= 0) { + bbox_print(PRINT_ERR, "Get task number failed.\n"); + goto errout; + } + + // 对线程执行ptrace并运行 + iRet = BBOX_PtraceAndRun(pstArgs, iMaxThreadCount, szProcSelfTask); + if (iRet != RET_OK) { + bbox_print(PRINT_ERR, "ptrace task and run failed.\n"); + goto errout; + } + + // 打印提示信息,获取信息成功 + bbox_print(PRINT_TIP, "Get information success.\n"); + + // 关闭socket + sys_close(iMaker); + + // 如果pstArgs的结果不等于RET_OK,则调用BBOX_PrintFailedLog函数打印失败日志 + if (RET_OK != pstArgs->iResult) { + BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2)); + } + + // 如果pstArgs的回调函数不为空,则调用回调函数 + if (pstArgs->pDoneCallback != NULL) { + pstArgs->pDoneCallback(pstArgs->pDoneArgs); + } + + // 退出线程,返回值为0 + sys_exit(0); + +// 错误处理 +errout: + if (iMaker > 0) { + sys_close(iMaker); + } + + // 打印错误信息 + bbox_print(PRINT_ERR, "Get information failed.\n"); + + // 设置pstArgs的结果为-1并保存错误号 + pstArgs->iResult = -1; + pstArgs->iError = errno; + + // 调用BBOX_PrintFailedLog函数打印失败日志 + BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2)); + + // 如果pstArgs的回调函数不为空,则调用回调函数 + if (pstArgs->pDoneCallback != NULL) { + pstArgs->pDoneCallback(pstArgs->pDoneArgs); + } + + // 退出线程,返回值为1 + sys_exit(1); +} + +/* +function name: BBOX_GetClonePidResult +description: The function get the status of child process at first, then according to it assign pstArgs's + member variables iError and iResult appropriate values. +arguments: The first argument is a integer named iClonePid, it represents the pid of child process. + The second argument is a structure pointer named pstArgs, its type is struct BBOX_ListParams*, + what matters is its member variable callback function pointer and thread infomation. + The third argument is a integer indicating error code. +return value: An integer, if function work normally, the value is RET_OK, else is RET_ERR. +note: none +date: 2022/8/3 +contact tel: 18720816902 +*/ +/* + * 获取克隆进程的结果并处理 + */ +s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 iCloneErrno) +{ + s32 iStatus = 0; + s32 iRet = 0; + + if (iClonePid < 0) { + + bbox_print(PRINT_ERR, "克隆进程失败,无法创建子进程,errno = %d。\n", iCloneErrno); + + BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2)); + if (pstArgs->pDoneCallback != NULL) { + pstArgs->pDoneCallback(pstArgs->pDoneArgs); + } + + return RET_ERR; + } + + /* 等待子进程退出 */ + while ((iRet = sys_waitpid(iClonePid, &iStatus, __WALL)) < 0 && errno == EINTR) { + continue; + } + + bbox_print(PRINT_LOG, "克隆进程的pid %d 返回值为 %x,状态为 %x\n", iClonePid, iRet, WIFEXITED(iStatus)); + + if (iRet < 0) { + pstArgs->iError = errno; + pstArgs->iResult = -1; + } else if (WIFEXITED(iStatus)) { + switch (WEXITSTATUS(iStatus)) { + case 0: + break; + case 2: + pstArgs->iError = EFAULT; + pstArgs->iResult = -1; + break; + case 3: + pstArgs->iError = EPERM; + pstArgs->iResult = 1; + break; + default: + pstArgs->iError = ECHILD; + pstArgs->iResult = -1; + break; + } + } else if (!WIFEXITED(iStatus)) { + pstArgs->iError = EFAULT; + pstArgs->iResult = -1; + bbox_print(PRINT_ERR, "WIFEXITED 状态判断失败"); + } else { + pstArgs->iError = iCloneErrno; + pstArgs->iResult = -1; + bbox_print(PRINT_ERR, "WIFEXITED 错误,errno = %d\n", iCloneErrno); + } + + return RET_OK; +} +/* + * get all threads and run specify function + */ +/* + * 获取所有线程并运行指定函数 + */ +s32 BBOX_GetAllThreads(GET_THREAD_TYPE enType, BBOX_GetAllThreadDone pDone, void* pDoneArgs, BBOX_GetAllThreadsCallBack pCallback, ...) +{ + struct BBOX_ListParams stArgs; + struct kernel_sigset_t stSigBlocked; + struct kernel_sigset_t stSigOld; + s32 iDumpable = 1; + s32 iSigNo; + s32 iCloneErrno; + pid_t ClonePid; + + errno = 0; + + if (BBOX_AtomicIncReturn(&g_isBusy) > 1) { + BBOX_AtomicDec(&g_isBusy); + + bbox_print(PRINT_ERR, "Dump任务正在运行中。\n"); + + errno = EALREADY; + return -1; + } + + if (enType >= GET_TYPE_BUTT || pCallback == NULL) { + bbox_print(PRINT_ERR, "参数无效,enType = %d,可能 pCallback 为空", enType); + BBOX_AtomicDec(&g_isBusy); + return -1; + } + + errno_t rc = memset_s(&stArgs, sizeof(stArgs), 0, sizeof(stArgs)); + securec_check_c(rc, "\0", "\0"); + rc = memset_s(&stSigBlocked, sizeof(stSigBlocked), 0, sizeof(stSigBlocked)); + securec_check_c(rc, "\0", "\0"); + rc = memset_s(&stSigOld, sizeof(stSigOld), 0, sizeof(stSigOld)); + securec_check_c(rc, "\0", "\0"); + + va_start(stArgs.ap, pCallback); + + /* 清空新栈 */ + rc = memset_s(g_szAltStackMem, BBOX_ALT_STACKSIZE, 0, BBOX_ALT_STACKSIZE); + securec_check_c(rc, "\0", "\0"); + /* 保留 32K */ + BBOX_ReserveZeroStack(1024 * 32); + + /* 检查并设置dump标志 */ + iDumpable = sys_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); + if (!iDumpable) { + sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + } + + /* 设置dump线程的启动参数 */ + stArgs.iResult = -1; + stArgs.iError = 0; + stArgs.pAltStackMem = g_szAltStackMem; + stArgs.pCallBack = pCallback; + stArgs.pDoneCallback = pDone; + stArgs.pDoneArgs = pDoneArgs; + stArgs.enGetType = enType; + + /* 暂停所有信号 */ + sys_sigfillset(&stSigBlocked); + for (iSigNo = 0; iSigNo < (s32)(sizeof(iSyncSignals) / sizeof(*iSyncSignals)); iSigNo++) { + sys_sigdelset(&stSigBlocked, iSyncSignals[iSigNo]); + } + + /* 阻塞所有信号 */ + if (sys_sigprocmask(SIG_BLOCK, &stSigBlocked, &stSigOld)) { + stArgs.iError = errno; + stArgs.iResult = -1; + bbox_print(PRINT_ERR, "sys_sigprocmask 错误,errno = %d\n", errno); + goto errout; + } + + /* 创建子进程并运行导出线程信息的函数 */ + if (GET_TYPE_DUMP == enType) { + ClonePid = BBOX_CloneRun(CLONE_VM | CLONE_FS | CLONE_FILES, (s32(*)(void*))BBOX_ListThread, &stArgs); + } else { + /* 如果类型是快照,则复制VMA */ + ClonePid = BBOX_CloneRun(CLONE_FS | CLONE_FILES, (s32(*)(void*))BBOX_ListThread, &stArgs); + } + + iCloneErrno = errno; + + /* 恢复信号 */ + sys_sigprocmask(SIG_SETMASK, &stSigOld, &stSigOld); + + if (BBOX_GetClonePidResult(ClonePid, &stArgs, iCloneErrno) != RET_OK) { + bbox_print(PRINT_ERR, "BBOX_GetClonePidResult 错误\n"); + } + +errout: + BBOX_AtomicDec(&g_isBusy); + + if (!iDumpable) { + sys_prctl(PR_SET_DUMPABLE, iDumpable, 0, 0, 0); + } + + va_end(stArgs.ap); + + errno = stArgs.iError; + return stArgs.iResult; +} -- 2.34.1 From 4c780da35d48dbfcfe13e29a9d343122dccf3534 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 21:01:30 +0800 Subject: [PATCH 32/56] Delete 'src/gausskernel/cbb/bbox/gs_bbox.cpp' --- src/gausskernel/cbb/bbox/gs_bbox.cpp | 494 --------------------------- 1 file changed, 494 deletions(-) delete mode 100644 src/gausskernel/cbb/bbox/gs_bbox.cpp diff --git a/src/gausskernel/cbb/bbox/gs_bbox.cpp b/src/gausskernel/cbb/bbox/gs_bbox.cpp deleted file mode 100644 index 405a30b92..000000000 --- a/src/gausskernel/cbb/bbox/gs_bbox.cpp +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * --------------------------------------------------------------------------------------- - * - * gs_bbox.cpp - * - * IDENTIFICATION - * src/gausskernel/cbb/bbox/gs_bbox.cpp - * - * --------------------------------------------------------------------------------------- - */ - -#include "bbox.h" -#include "bbox_types.h" - -#include "postgres.h" - -#include "gs_bbox.h" -#include "libpq/pqsignal.h" -#include "miscadmin.h" -#include "postmaster/postmaster.h" -#include "utils/elog.h" -#include "utils/guc.h" -#include "utils/fatal_err.h" - -#define BBOX_PATH_SIZE 512 -#define DEFAULT_BLACKLIST_MASK (0xFFFFFFFFFFFFFFFF) - -#define INVALID_TID (-1) - -static char g_bbox_dump_path[BBOX_PATH_SIZE] = {0}; - -#ifdef ENABLE_UT -#define static -int bbox_handler_exit = 0; -#endif - - -BlacklistItem g_blacklist_items[] = { - {SHARED_BUFFER, "SHARED_BUFFER", true}, - {XLOG_BUFFER, "XLOG_BUFFER", true}, - {DW_BUFFER, "DW_BUFFER", false}, - {XLOG_MESSAGE_SEND, "XLOG_MESSAGE_SEND", false}, - {DATA_MESSAGE_SEND, "DATA_MESSAGE_SEND", false}, - {WALRECIVER_CTL_BLOCK, "WALRECIVER_CTL_BLOCK", false}, - {DATA_WRITER_QUEUE, "DATA_WRITER_QUEUE", false} -}; - -/* -function name: coredump_handler -description: When a program is abnormal, but the exception appears in the core of process and wasn't caught, - The function will generate a file to store the information about memory of process, status of register - and running stack. -arguments: The first argument is an integer indicating signal code that usually used in program of processing - signal as variable. - The second argument is a structure pointer of type siginfo_t*, the memory that this pointer - directs stores comprehensive information about signal, for example, which process sends - and which user sends. - The third argument is a pointer of type void*, other kinds of pointers can directly used here. -return value: void -note: none -date: 2022/8/4 -contact tel: 18720816902 -*/ -static void coredump_handler(int sig, siginfo_t *si, void *uc) -{ - static volatile int64 first_tid = INVALID_TID; - int64 cur_tid = (int64)pthread_self(); - - if (first_tid == INVALID_TID && - __sync_bool_compare_and_swap(&first_tid, INVALID_TID, cur_tid)) { - /* Only first fatal error will set db state and generate fatal error log */ - (void)SetDBStateFileState(COREDUMP_STATE, false); - if (g_instance.attr.attr_common.enable_ffic_log) { - (void)gen_err_msg(sig, si, (ucontext_t *)uc); - } - } else { - /* - * Subsequent fatal error will go to here. If it comes from different thread, - * wait until first error handler end, and if it is a reentry, terminate process. - */ - if (first_tid != cur_tid) { - (void)pause(); - } - } - - (void)pqsignal(sig, SIG_DFL); - (void)raise(sig); -} - -/* -function name: bbox_handler -description: Handle signal conditions for bbox. -arguments: The first argument is an integer indicating signal code that usually used in program of processing - signal as variable. - The second argument is a structure pointer of type siginfo_t*, the memory that this pointer - directs stores comprehensive information about signal, for example, which process sends - and which user sends. - The third argument is a pointer of type void*, other kinds of pointers can directly used here. -return value: void -note: none -date: 2022/8/4 -contact tel: 18720816902 -*/ -static void bbox_handler(int sig, siginfo_t *si, void *uc) -{ - static volatile int64 first_tid = INVALID_TID; - int64 cur_tid = (int64)pthread_self(); - - if (first_tid == INVALID_TID && - __sync_bool_compare_and_swap(&first_tid, INVALID_TID, cur_tid)) { - (void)SetDBStateFileState(COREDUMP_STATE, false); - if (g_instance.attr.attr_common.enable_ffic_log) { - (void)gen_err_msg(sig, si, (ucontext_t *)uc); - } - -#ifndef ENABLE_MEMORY_CHECK - sigset_t intMask; - sigset_t oldMask; - - sigfillset(&intMask); - pthread_sigmask(SIG_SETMASK, &intMask, &oldMask); - - (void)BBOX_CreateCoredump(NULL); -#else -#ifndef ENABLE_UT - if (sig != SIGABRT) - abort(); -#endif -#endif - -#ifdef ENABLE_UT - if (bbox_handler_exit == 0) -#endif - _exit(0); - } else { - if (first_tid != cur_tid) { - (void)pause(); - } - } -} - -/* -function name: get_bbox_coredump_pattern_path -description: Get the core dump file's path from the file "/proc/sys/kernel/core_pattern". -arguments: The first argument is a pointer to string, we use it to store core dump file's path acquired - from the file "/proc/sys/kernel/core_pattern", the next argument is the number of characters - reading from the file "/proc/sys/kernel/core_pattern", all len-1 characters or less if appear '\n'. -return value: void -note: none -date: 2022/8/4 -contact tel: 18720816902 -*/ -static void get_bbox_coredump_pattern_path(char* path, Size len) -{ - FILE* fp = NULL; - char* p = NULL; - struct stat stat_buf; - - if ((fp = fopen("/proc/sys/kernel/core_pattern", "r")) == NULL) { - write_stderr("cannot open file: /proc/sys/kernel/core_pattern.\n"); - return; - } - - if (fgets(path, len, fp) == NULL) { - fclose(fp); - write_stderr("failed to get the core pattern path.\n "); - return; - } - fclose(fp); - - if ((p = strrchr(path, '/')) == NULL) { /* a relative-path file */ - *path = '\0'; - } else { /* an absolute-path file */ - *(++p) = '\0'; - if (stat(path, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode) || access(path, W_OK) != 0) { - write_stderr("The core dump path is an invalid directory\n"); - *path = '\0'; - } - } -} - -/* -function name: build_bbox_corepath -description: Get the core dump file's path. -arguments: The first argument is a pointer to string, we use it to store core dump file's path, - the next argument is the size of the path's name, the last argument is a pointer - to string that indicates maybe store a path to configure the core dump file. -return value: void -note: none -date: 2022/8/4 -contact tel: 18720816902 -*/ -static void build_bbox_corepath(char *bbox_core_path, Size path_size, char *config_path) -{ - struct stat stat_buf; - - /* - * the guc parameter bbox_dump_path is set to NULL as default. - * bbox_dump_path has to be a valid directory, if it is altered by users. - */ - if (config_path != NULL && config_path[0] != '\0') { - if (stat(config_path, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode) || access(config_path, W_OK) != 0) { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("bbox_dump_path %s is an invalid directory!\n", config_path))); - - /* if bbox_dump_path is invalid, the path of core dump will be set as default. */ - get_bbox_coredump_pattern_path(bbox_core_path, path_size); - } else { - errno_t rc = strcpy_s(bbox_core_path, path_size, config_path); - securec_check(rc, "\0", "\0"); - } - } else { - /* - * default path of the core dump will be obtained - * by reading the file "/proc/sys/kernel/core_pattern" - */ - get_bbox_coredump_pattern_path(bbox_core_path, path_size); - } -} - -/* - * check_bbox_corepath - check coredump path for bbox - */ -bool check_bbox_corepath(char** newval, void** extra, GucSource source) -{ - if (t_thrd.proc_cxt.MyProcPid != PostmasterPid) - return true; - - char core_dump_path[BBOX_PATH_SIZE] = {0}; - - /* determine which path is used for bbox core dump file */ - build_bbox_corepath(core_dump_path, sizeof(core_dump_path), (newval != NULL) ? *newval : NULL); - - if (core_dump_path[0] != '\0' && BBOX_SetCoredumpPath(core_dump_path) == RET_OK) { - ereport(LOG, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("bbox_dump_path is set to %s", core_dump_path))); - } - - char* result = (char*)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX), BBOX_PATH_SIZE); - if (newval != NULL && *newval != NULL) - pfree(*newval); - - errno_t rc = strcpy_s(result, BBOX_PATH_SIZE, core_dump_path); - securec_check(rc, "\0", "\0"); - - rc = strcpy_s(g_bbox_dump_path, sizeof(g_bbox_dump_path), result); - securec_check(rc, "\0", "\0"); - - if (newval != NULL) { - *newval = result; - } else { - free(result); - } - - return true; -} - -/* - * assign_bbox_corepath - set coredump path for bbox - */ -void assign_bbox_corepath(const char* newval, void* extra) -{ - if (t_thrd.proc_cxt.MyProcPid != PostmasterPid) - return; -} - -/* -function name: show_bbox_dump_path -description: Get the dump file's path. -arguments: void -return value: A pointer of type const char*, directing the path to dump or NULL. -note: none -date: 2022/8/4 -contact tel: 18720816902 -*/ -const char* show_bbox_dump_path(void) -{ - const char* path = g_bbox_dump_path; - - return (path != NULL) ? path : ""; -} - -/* -function name: split_string_into_blacklist -description: Get all strings been divided into character ',' in source string. -arguments: A pointer of type const char*, directing the source string. -return value: A pointer of type static List*. -note: none -date: 2022/8/4 -contact tel: 18720816902 -*/ -static List* split_string_into_blacklist(const char* source) -{ - List *result = NIL; - char *str = pstrdup(source); - char *first_ch = str; - int len = strlen(str) + 1; - - for (int i = 0; i < len; i++) { - if (str[i] == ',' || str[i] == '\0') { - /* replace ',' with '\0'. */ - str[i] = '\0'; - - /* copy this into result. */ - result = lappend(result, pstrdup(first_ch)); - - /* move to the head of next string. */ - first_ch = str + i + 1; - i++; - } - } - pfree(str); - - return result; -} - -bool check_bbox_blacklist(char** newval, void** extra, GucSource source) -{ - if (t_thrd.proc_cxt.MyProcPid != PostmasterPid) - return true; - - List *result = split_string_into_blacklist(*newval); - ListCell *lc = NULL; - uint64 mask = 0; - size_t i; - - foreach(lc, result) { - if (strcmp("", (char*)lfirst(lc)) == 0) { - continue; - } - for (i = 0; i < sizeof(g_blacklist_items) / sizeof(BlacklistItem); i++) { - if (strcmp(g_blacklist_items[i].blacklist_name, (char*)lfirst(lc)) == 0) { - mask = mask | BLACKLIST_ITEM_MASK(g_blacklist_items[i].blacklist_ID); - break; - } - } - if (i == sizeof(g_blacklist_items) / sizeof(BlacklistItem)) { - ereport(WARNING, - (errmsg("blacklist item %s does not exist, so it is ignored.", (char*)lfirst(lc)))); - } - } - list_free_deep(result); - - *extra = MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX), sizeof(uint64)); - if (*extra == NULL) - return false; - - *((uint64*)*extra) = (mask == 0) ? DEFAULT_BLACKLIST_MASK : mask; - return true; -} - -void assign_bbox_blacklist(const char* newval, void* extra) -{ - if (t_thrd.proc_cxt.MyProcPid == PostmasterPid) { - g_instance.attr.attr_common.bbox_blacklist_mask = *((uint64*)extra); - } -} - -const char* show_bbox_blacklist() -{ - StringInfoData str; - - initStringInfo(&str); - for (size_t i = 0; i < sizeof(g_blacklist_items) / sizeof(BlacklistItem); i++) { - if ((BLACKLIST_ITEM_MASK(g_blacklist_items[i].blacklist_ID) & BBOX_BLACKLIST) != 0) { - appendStringInfo(&str, "%s,", g_blacklist_items[i].blacklist_name); - } - } - if (str.len >= 0) { - str.data[--str.len] = '\0'; - } - - return str.data; -} - -/* - * assign_bbox_coredump - set coredump for bbox or not - */ -void assign_bbox_coredump(const bool newval, void* extra) -{ - if (t_thrd.proc_cxt.MyProcPid != PostmasterPid) - return; - - if (newval && !FencedUDFMasterMode) { - (void)install_signal(SIGABRT, bbox_handler); - (void)install_signal(SIGBUS, bbox_handler); - (void)install_signal(SIGILL, bbox_handler); - (void)install_signal(SIGSEGV, bbox_handler); - } else { - (void)install_signal(SIGABRT, coredump_handler); - (void)install_signal(SIGBUS, coredump_handler); - (void)install_signal(SIGILL, coredump_handler); - (void)install_signal(SIGSEGV, coredump_handler); - } -} - -/* - * do initilaization for dumping core file - */ -void bbox_initialize() -{ - char core_dump_path[BBOX_PATH_SIZE] = {0}; - - /* determine path which is used for bbox core dump file */ - build_bbox_corepath(core_dump_path, sizeof(core_dump_path), - u_sess->attr.attr_common.bbox_dump_path); - - if (u_sess->attr.attr_common.enable_bbox_dump && *core_dump_path != '\0' && - CheckFilenameValid(core_dump_path) == RET_OK && - BBOX_SetCoredumpPath(core_dump_path) == 0) { - write_stderr("bbox_dump_path is set to %s\n", core_dump_path); - } - - /* - * no matter bbox_dump_count is default (8) or set by users, call function BBOX_SetCoreFileCount. - * Note: bbox_dump_count cannot be smaller than 1. - */ - if (u_sess->attr.attr_common.bbox_dump_count != 0 && - BBOX_SetCoreFileCount(u_sess->attr.attr_common.bbox_dump_count) != 0) { - write_stderr("failed to set coredump count.\n"); - } - - assign_bbox_coredump(u_sess->attr.attr_common.enable_bbox_dump, NULL); -} - -/* - * add an blacklist item to exclude it from core file. - * void *pAddress : the head address of excluded memory - * u64 uilen : memory size - */ -void bbox_blacklist_add(BlacklistIndex item, void* addr, uint64 size) -{ - if (t_thrd.proc_cxt.MyProcPid == PostmasterPid || !g_blacklist_items[item].pm_only) { - if (BBOX_AddBlackListAddress(addr, size) != RET_OK) { - ereport(WARNING, - (errmsg("failed to add bbox blacklist item [%s]", g_blacklist_items[item].blacklist_name))); - } - } -} - -/* - * remove an blacklist item. - * void *pAddress : the head address of excluded memory - */ -void bbox_blacklist_remove(BlacklistIndex item, void* addr) -{ - if (addr != NULL && BBOX_RmvBlackListAddress(addr) != RET_OK) { - ereport(WARNING, - (errmsg("failed to remove bbox blacklist item [%s]", g_blacklist_items[item].blacklist_name))); - } -} - -/* -function name: CheckFilenameValid -description: Check if the filename is in line with norms, or if dangerous characters appear - the filename is invalid. -arguments: A pointer to string indicating filename. -return value: An integer, if function works normally, the value is RET_OK, else it's RET_ERR. -note: none -date: 2022/8/4 -contact tel: 18720816902 -*/ -int CheckFilenameValid(const char* inputEnvValue) -{ - const int maxLen = 1024; - - const char* dangerCharacterList[] = {";", "`", "\\", "'", "\"", ">", "<", "$", "&", "|", "!", "\n", NULL}; - int i = 0; - - if (inputEnvValue == NULL || strlen(inputEnvValue) >= maxLen) { - return RET_ERR; - } - - for (i = 0; dangerCharacterList[i] != NULL; i++) { - if (strstr((const char*)inputEnvValue, dangerCharacterList[i])) { - return RET_ERR; - } - } - return RET_OK; -} - -- 2.34.1 From 3b6930c9fdd1e199e00764141a74b7fc2fa2eed1 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 21:02:04 +0800 Subject: [PATCH 33/56] ADD file via upload --- src/gausskernel/cbb/bbox/gs_bbox.cpp | 561 +++++++++++++++++++++++++++ 1 file changed, 561 insertions(+) create mode 100644 src/gausskernel/cbb/bbox/gs_bbox.cpp diff --git a/src/gausskernel/cbb/bbox/gs_bbox.cpp b/src/gausskernel/cbb/bbox/gs_bbox.cpp new file mode 100644 index 000000000..f094a4c8e --- /dev/null +++ b/src/gausskernel/cbb/bbox/gs_bbox.cpp @@ -0,0 +1,561 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * --------------------------------------------------------------------------------------- + * + * gs_bbox.cpp + * + * IDENTIFICATION + * src/gausskernel/cbb/bbox/gs_bbox.cpp + * + * --------------------------------------------------------------------------------------- + */ + +#include "bbox.h" +#include "bbox_types.h" + +#include "postgres.h" + +#include "gs_bbox.h" +#include "libpq/pqsignal.h" +#include "miscadmin.h" +#include "postmaster/postmaster.h" +#include "utils/elog.h" +#include "utils/guc.h" +#include "utils/fatal_err.h" + +#define BBOX_PATH_SIZE 512 +#define DEFAULT_BLACKLIST_MASK (0xFFFFFFFFFFFFFFFF) + +#define INVALID_TID (-1) + +static char g_bbox_dump_path[BBOX_PATH_SIZE] = {0}; + +#ifdef ENABLE_UT +#define static +int bbox_handler_exit = 0; +#endif + + +BlacklistItem g_blacklist_items[] = { + {SHARED_BUFFER, "SHARED_BUFFER", true}, + {XLOG_BUFFER, "XLOG_BUFFER", true}, + {DW_BUFFER, "DW_BUFFER", false}, + {XLOG_MESSAGE_SEND, "XLOG_MESSAGE_SEND", false}, + {DATA_MESSAGE_SEND, "DATA_MESSAGE_SEND", false}, + {WALRECIVER_CTL_BLOCK, "WALRECIVER_CTL_BLOCK", false}, + {DATA_WRITER_QUEUE, "DATA_WRITER_QUEUE", false} +}; + +/* +function name: coredump_handler +description: When a program is abnormal, but the exception appears in the core of process and wasn't caught, + The function will generate a file to store the information about memory of process, status of register + and running stack. +arguments: The first argument is an integer indicating signal code that usually used in program of processing + signal as variable. + The second argument is a structure pointer of type siginfo_t*, the memory that this pointer + directs stores comprehensive information about signal, for example, which process sends + and which user sends. + The third argument is a pointer of type void*, other kinds of pointers can directly used here. +return value: void +note: none +date: 2022/8/4 +contact tel: 18720816902 +*/ +/** + * 此函数是用于核心转储信号的信号处理器。 + * 在接收到核心转储信号时,将调用此函数。 + * + * 参数: + * - sig:信号编号 + * - si:指向一个结构的指针,包含有关信号的附加信息 + * - uc:指向一个结构的指针,包含信号被触发时的机器上下文 + */ +static void coredump_handler(int sig, siginfo_t *si, void *uc) { + // 此变量存储第一个遇到致命错误的线程的线程ID。 + static volatile int64 first_tid = INVALID_TID; + + // 获取当前线程的线程ID。 + int64 cur_tid = (int64)pthread_self(); + + // 检查是否为任何线程首次遇到的致命错误。 + if (first_tid == INVALID_TID && + __sync_bool_compare_and_swap(&first_tid, INVALID_TID, cur_tid)) { + /* 只有首个致命错误会设置数据库状态并生成致命错误日志 */ + // 将数据库状态文件设置为 COREDUMP_STATE,表示发生了核心转储。 + (void)SetDBStateFileState(COREDUMP_STATE, false); + + // 如果启用了 FFIC 日志,则生成一个错误消息。 + if (g_instance.attr.attr_common.enable_ffic_log) { + (void)gen_err_msg(sig, si, (ucontext_t *)uc); + } + } else { + /* + * 后续的致命错误将进入此处。如果来自不同的线程, + * 则等待第一个错误处理器结束,如果是重新进入,则终止进程。 + */ + + // 如果这不是第一个致命错误并且来自不同的线程, + // 则等待第一个错误处理器结束,如果是重新进入,则终止进程。 + if (first_tid != cur_tid) { + (void)pause(); + } + } + + // 恢复信号的默认处理器。 + (void)pqsignal(sig, SIG_DFL); + + // 再次触发该信号,以调用默认的信号处理器。 + (void)raise(sig); +} + +/* +function name: bbox_handler +description: Handle signal conditions for bbox. +arguments: The first argument is an integer indicating signal code that usually used in program of processing + signal as variable. + The second argument is a structure pointer of type siginfo_t*, the memory that this pointer + directs stores comprehensive information about signal, for example, which process sends + and which user sends. + The third argument is a pointer of type void*, other kinds of pointers can directly used here. +return value: void +note: none +date: 2022/8/4 +contact tel: 18720816902 +*/ +static void bbox_handler(int sig, siginfo_t *si, void *uc) +{ + static volatile int64 first_tid = INVALID_TID; + int64 cur_tid = (int64)pthread_self(); + + if (first_tid == INVALID_TID && + __sync_bool_compare_and_swap(&first_tid, INVALID_TID, cur_tid)) { + (void)SetDBStateFileState(COREDUMP_STATE, false); + if (g_instance.attr.attr_common.enable_ffic_log) { + (void)gen_err_msg(sig, si, (ucontext_t *)uc); + } + +#ifndef ENABLE_MEMORY_CHECK + sigset_t intMask; + sigset_t oldMask; + + sigfillset(&intMask); + pthread_sigmask(SIG_SETMASK, &intMask, &oldMask); + + (void)BBOX_CreateCoredump(NULL); +#else +#ifndef ENABLE_UT + if (sig != SIGABRT) + abort(); +#endif +#endif + +#ifdef ENABLE_UT + if (bbox_handler_exit == 0) +#endif + _exit(0); + } else { + if (first_tid != cur_tid) { + (void)pause(); + } + } +} + +/* +function name: get_bbox_coredump_pattern_path +description: Get the core dump file's path from the file "/proc/sys/kernel/core_pattern". +arguments: The first argument is a pointer to string, we use it to store core dump file's path acquired + from the file "/proc/sys/kernel/core_pattern", the next argument is the number of characters + reading from the file "/proc/sys/kernel/core_pattern", all len-1 characters or less if appear '\n'. +return value: void +note: none +date: 2022/8/4 +contact tel: 18720816902 +*/ +/** + * 此函数用于获取核心转储模式的路径。 + * + * 参数: + * - path:用于存储核心转储模式路径的缓冲区 + * - len:缓冲区的大小 + */ +static void get_bbox_coredump_pattern_path(char* path, Size len) { + FILE* fp = NULL; + char* p = NULL; + struct stat stat_buf; + + if ((fp = fopen("/proc/sys/kernel/core_pattern", "r")) == NULL) { + // 打开文件失败,写入错误提示信息,并返回。 + write_stderr("无法打开文件:/proc/sys/kernel/core_pattern。\n"); + return; + } + + if (fgets(path, len, fp) == NULL) { + // 获取核心模式路径失败,关闭文件,写入错误提示信息,并返回。 + fclose(fp); + write_stderr("无法获取核心模式路径。\n"); + return; + } + fclose(fp); + + if ((p = strrchr(path, '/')) == NULL) { /* 相对路径文件 */ + *path = '\0'; + } else { /* 绝对路径文件 */ + *(++p) = '\0'; + // 检查路径是否有效并且具有写权限。 + if (stat(path, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode) || access(path, W_OK) != 0) { + // 核心转储路径是无效的目录,写入错误提示信息,并清空路径。 + write_stderr("核心转储路径是无效的目录。\n"); + *path = '\0'; + } + } +} + +/* +function name: build_bbox_corepath +description: Get the core dump file's path. +arguments: The first argument is a pointer to string, we use it to store core dump file's path, + the next argument is the size of the path's name, the last argument is a pointer + to string that indicates maybe store a path to configure the core dump file. +return value: void +note: none +date: 2022/8/4 +contact tel: 18720816902 +*/ +/** + * 此函数用于构建 bbox 核心转储路径。 + * + * 参数: + * - bbox_core_path:用于存储 bbox 核心转储路径的缓冲区 + * - path_size:缓冲区的大小 + * - config_path:指定的配置路径,如果为 NULL,则使用默认路径 + */ +static void build_bbox_corepath(char *bbox_core_path, Size path_size, char *config_path) { + struct stat stat_buf; + + /* + * 默认情况下,guc 参数 bbox_dump_path 设置为 NULL。 + * 如果用户修改了 bbox_dump_path,它必须是一个有效的目录。 + */ + if (config_path != NULL && config_path[0] != '\0') { + if (stat(config_path, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode) || access(config_path, W_OK) != 0) { + ereport(WARNING, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("bbox_dump_path %s 是一个无效的目录!\n", config_path))); + + /* 如果 bbox_dump_path 是无效的,将使用默认的核心转储路径。 */ + get_bbox_coredump_pattern_path(bbox_core_path, path_size); + } else { + errno_t rc = strcpy_s(bbox_core_path, path_size, config_path); + securec_check(rc, "\0", "\0"); + } + } else { + /* + * 默认情况下,核心转储路径将从文件 "/proc/sys/kernel/core_pattern" 中获取。 + */ + get_bbox_coredump_pattern_path(bbox_core_path, path_size); + } +} + +/* + * check_bbox_corepath - 检查 bbox 的核心转储路径 + */ +bool check_bbox_corepath(char** newval, void** extra, GucSource source) { + if (t_thrd.proc_cxt.MyProcPid != PostmasterPid) + return true; + + char core_dump_path[BBOX_PATH_SIZE] = {0}; + + /* 确定用于 bbox 核心转储文件的路径 */ + build_bbox_corepath(core_dump_path, sizeof(core_dump_path), (newval != NULL) ? *newval : NULL); + + if (core_dump_path[0] != '\0' && BBOX_SetCoredumpPath(core_dump_path) == RET_OK) { + ereport(LOG, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("bbox_dump_path 设置为 %s", core_dump_path))); + } + + char* result = (char*)MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX), BBOX_PATH_SIZE); + if (newval != NULL && *newval != NULL) + pfree(*newval); + + errno_t rc = strcpy_s(result, BBOX_PATH_SIZE, core_dump_path); + securec_check(rc, "\0", "\0"); + + rc = strcpy_s(g_bbox_dump_path, sizeof(g_bbox_dump_path), result); + securec_check(rc, "\0", "\0"); + + if (newval != NULL) { + *newval = result; + } else { + free(result); + } + + return true; +} + +/* + * assign_bbox_corepath - set coredump path for bbox + */ +void assign_bbox_corepath(const char* newval, void* extra) +{ + if (t_thrd.proc_cxt.MyProcPid != PostmasterPid) + return; +} + +/* +function name: show_bbox_dump_path +description: Get the dump file's path. +arguments: void +return value: A pointer of type const char*, directing the path to dump or NULL. +note: none +date: 2022/8/4 +contact tel: 18720816902 +*/ +const char* show_bbox_dump_path(void) +{ + const char* path = g_bbox_dump_path; + + return (path != NULL) ? path : ""; +} + +/* +function name: split_string_into_blacklist +description: Get all strings been divided into character ',' in source string. +arguments: A pointer of type const char*, directing the source string. +return value: A pointer of type static List*. +note: none +date: 2022/8/4 +contact tel: 18720816902 +*/ +/** + * 将一个字符串拆分为黑名单列表。 + * + * 参数: + * - source:要拆分的源字符串 + * + * 返回值: + * - 拆分后的黑名单列表 + */ +static List* split_string_into_blacklist(const char* source) +{ + List *result = NIL; // 初始化列表为空 + char *str = pstrdup(source); // 复制源字符串 + char *first_ch = str; // 指向第一个字符的指针 + int len = strlen(str) + 1; // 字符串长度加1,包括结尾的空字符 + + for (int i = 0; i < len; i++) { + if (str[i] == ',' || str[i] == '\0') { + /* 将 ',' 替换为 '\0' */ + str[i] = '\0'; + + /* 将该字符串添加到结果列表中 */ + result = lappend(result, pstrdup(first_ch)); + + /* 移动到下一个字符串的开头 */ + first_ch = str + i + 1; + i++; + } + } + pfree(str); // 释放复制的字符串内存 + + return result; // 返回拆分后的黑名单列表 +} + +/** + * 检查 bbox 黑名单配置项。 + * + * 参数: + * - newval:新设置的值 + * - extra:附加信息 + * - source:配置项的来源 + * + * 返回值: + * - 如果检查通过,返回 true;否则返回 false + */ +bool check_bbox_blacklist(char** newval, void** extra, GucSource source) +{ + if (t_thrd.proc_cxt.MyProcPid != PostmasterPid) + return true; + + List *result = split_string_into_blacklist(*newval); // 将配置项的值拆分为黑名单列表 + ListCell *lc = NULL; + uint64 mask = 0; // 用于保存黑名单掩码 + size_t i; + + foreach(lc, result) { + if (strcmp("", (char*)lfirst(lc)) == 0) { + continue; + } + for (i = 0; i < sizeof(g_blacklist_items) / sizeof(BlacklistItem); i++) { + if (strcmp(g_blacklist_items[i].blacklist_name, (char*)lfirst(lc)) == 0) { + mask = mask | BLACKLIST_ITEM_MASK(g_blacklist_items[i].blacklist_ID); + break; + } + } + if (i == sizeof(g_blacklist_items) / sizeof(BlacklistItem)) { + ereport(WARNING, + (errmsg("黑名单项 %s 不存在,已忽略。", (char*)lfirst(lc)))); + } + } + list_free_deep(result); // 释放拆分后的黑名单列表内存 + + *extra = MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX), sizeof(uint64)); + if (*extra == NULL) + return false; + + *((uint64*)*extra) = (mask == 0) ? DEFAULT_BLACKLIST_MASK : mask; // 设置附加信息为黑名单掩码 + return true; +} + +/** + * 设置 bbox 黑名单。 + * + * 参数: + * - newval:新设置的值 + * - extra:附加信息 + */ +void assign_bbox_blacklist(const char* newval, void* extra) +{ + if (t_thrd.proc_cxt.MyProcPid == PostmasterPid) { + g_instance.attr.attr_common.bbox_blacklist_mask = *((uint64*)extra); // 设置 bbox 黑名单掩码 + } +} + +/** + * 显示当前的 bbox 黑名单。 + * + * 返回值: + * - 当前的 bbox 黑名单字符串 + */ +const char* show_bbox_blacklist() +{ + StringInfoData str; + + initStringInfo(&str); // 初始化字符串信息 + for (size_t i = 0; i < sizeof(g_blacklist_items) / sizeof(BlacklistItem); i++) { + if ((BLACKLIST_ITEM_MASK(g_blacklist_items[i].blacklist_ID) & BBOX_BLACKLIST) != 0) { + appendStringInfo(&str, "%s,", g_blacklist_items[i].blacklist_name); + } + } + if (str.len >= 0) { + str.data[--str.len] = '\0'; // 将最后一个逗号替换为结束符 + } + + return str.data; // 返回黑名单字符串 +} + +/** + * 设置是否进行 bbox 核心转储。 + * + * 参数: + * - newval:新设置的值 + * - extra:附加信息 + */ +void assign_bbox_coredump(const bool newval, void* extra) +{ + if (t_thrd.proc_cxt.MyProcPid != PostmasterPid) + return; + + if (newval && !FencedUDFMasterMode) { + (void)install_signal(SIGABRT, bbox_handler); // 安装信号处理程序 + (void)install_signal(SIGBUS, bbox_handler); + (void)install_signal(SIGILL, bbox_handler); + (void)install_signal(SIGSEGV, bbox_handler); + } else { + (void)install_signal(SIGABRT, coredump_handler); + (void)install_signal(SIGBUS, coredump_handler); + (void)install_signal(SIGILL, coredump_handler); + (void)install_signal(SIGSEGV, coredump_handler); + } +} + +/** + * 初始化 bbox 核心转储。 + */ +void bbox_initialize() +{ + char core_dump_path[BBOX_PATH_SIZE] = {0}; // 存储核心转储路径的缓冲区 + + /* 确定用于 bbox 核心转储文件的路径 */ + build_bbox_corepath(core_dump_path, sizeof(core_dump_path), + u_sess->attr.attr_common.bbox_dump_path); + + if (u_sess->attr.attr_common.enable_bbox_dump && *core_dump_path != '\0' && + CheckFilenameValid(core_dump_path) == RET_OK && + BBOX_SetCoredumpPath(core_dump_path) == 0) { + write_stderr("bbox_dump_path 设置为 %s\n", core_dump_path); // 打印核心转储路径 + } + + /* + * 无论 bbox_dump_count 是默认值 (8) 还是用户设置的值,都调用函数 BBOX_SetCoreFileCount。 + * 注意:bbox_dump_count 不能小于 1。 + */ + if (u_sess->attr.attr_common.bbox_dump_count != 0 && + BBOX_SetCoreFileCount(u_sess->attr.attr_common.bbox_dump_count) != 0) { + write_stderr("设置核心转储文件计数失败。\n"); // 打印设置核心转储文件计数失败信息 + } + + assign_bbox_coredump(u_sess->attr.attr_common.enable_bbox_dump, NULL); // 设置是否进行 bbox 核心转储 +} + +/* + * add an blacklist item to exclude it from core file. + * void *pAddress : the head address of excluded memory + * u64 uilen : memory size + */ +void bbox_blacklist_add(BlacklistIndex item, void* addr, uint64 size) +{ + if (t_thrd.proc_cxt.MyProcPid == PostmasterPid || !g_blacklist_items[item].pm_only) { + if (BBOX_AddBlackListAddress(addr, size) != RET_OK) { + ereport(WARNING, + (errmsg("failed to add bbox blacklist item [%s]", g_blacklist_items[item].blacklist_name))); + } + } +} + +/* + * 移除一个黑名单项。 + * void *pAddress : 要排除的内存的起始地址 + */ +void bbox_blacklist_remove(BlacklistIndex item, void* addr) +{ + if (addr != NULL && BBOX_RmvBlackListAddress(addr) != RET_OK) { + ereport(WARNING, + (errmsg("failed to remove bbox blacklist item [%s]", g_blacklist_items[item].blacklist_name))); + } +} + +/* +描述: 检查文件名是否符合规范,如果包含危险字符,则文件名无效。 +参数: 文件名的字符串指针。 +返回值: 整型,函数正常工作时返回 RET_OK,否则返回 RET_ERR。 +*/ +int CheckFilenameValid(const char* inputEnvValue) +{ + const int maxLen = 1024; + + const char* dangerCharacterList[] = {";", "`", "\\", "'", "\"", ">", "<", "$", "&", "|", "!", "\n", NULL}; + int i = 0; + + if (inputEnvValue == NULL || strlen(inputEnvValue) >= maxLen) { + return RET_ERR; + } + + for (i = 0; dangerCharacterList[i] != NULL; i++) { + if (strstr((const char*)inputEnvValue, dangerCharacterList[i])) { + return RET_ERR; + } + } + return RET_OK; +} -- 2.34.1 From 208c7133b2aeb951e27e6fa0aef2d1554e6719c2 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 21:05:10 +0800 Subject: [PATCH 34/56] Delete 'src/gausskernel/cbb/communication/libcomm.cpp' --- src/gausskernel/cbb/communication/libcomm.cpp | 2870 ----------------- 1 file changed, 2870 deletions(-) delete mode 100755 src/gausskernel/cbb/communication/libcomm.cpp diff --git a/src/gausskernel/cbb/communication/libcomm.cpp b/src/gausskernel/cbb/communication/libcomm.cpp deleted file mode 100755 index e1aff3ea5..000000000 --- a/src/gausskernel/cbb/communication/libcomm.cpp +++ /dev/null @@ -1,2870 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * libcomm.cpp - * - * IDENTIFICATION - * src/gausskernel/cbb/communication/libcomm.cpp - * - * ------------------------------------------------------------------------- - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libcomm_core/mc_tcp.h" -#include "libcomm_core/mc_poller.h" -#include "libcomm_utils/libcomm_thread.h" -#include "libcomm_utils/libcomm_lqueue.h" -#include "libcomm_utils/libcomm_queue.h" -#include "libcomm_utils/libcomm_lock_free_queue.h" -#include "distributelayer/streamCore.h" -#include "distributelayer/streamProducer.h" -#include "pgxc/poolmgr.h" -#include "libpq/auth.h" -#include "libpq/pqsignal.h" -#include "storage/ipc.h" -#include "utils/ps_status.h" -#include "utils/dynahash.h" - -#include "vecexecutor/vectorbatch.h" -#include "vecexecutor/vecnodes.h" -#include "executor/exec/execStream.h" -#include "miscadmin.h" -#include "gssignal/gs_signal.h" -#include "pgxc/pgxc.h" -#include "libcomm_common.h" - -#ifdef ENABLE_UT -#define static -#endif - -#define CHECKCONNSTATTIMEOUT 5 - -#ifndef MS_PER_S -#define MS_PER_S 1000 -#endif - - -/* hash tables */ -/* hash table to keep: ip + port -> connection status */ -static HTAB* g_htab_ip_state = NULL; -pthread_mutex_t g_htab_ip_state_lock; - -/* hash table to keep: nodename -> node id */ -static HTAB* g_htab_nodename_node_idx = NULL; -pthread_mutex_t g_htab_nodename_node_idx_lock; -static int nodename_count = 0; - -/* hash table to keep: fd_id -> node id */ -HTAB* g_htab_fd_id_node_idx = NULL; -pthread_mutex_t g_htab_fd_id_node_idx_lock; - -/* for wake up thread */ -static HTAB* g_htab_tid_poll = NULL; -pthread_mutex_t g_htab_tid_poll_lock; - -/* at receiver and sender: hash table to keep: socket -> socket version number, for socket management */ -static HTAB* g_htab_socket_version = NULL; -pthread_mutex_t g_htab_socket_version_lock; - -static ArrayLockFreeQueue g_memory_pool_queue; - -unsigned long IOV_DATA_SIZE = 1024 * 8; -unsigned long IOV_ITEM_SIZE = IOV_DATA_SIZE + sizeof(struct iovec) + sizeof(mc_lqueue_element); -unsigned long DEFULTMSGLEN = 1024 * 8; -unsigned long LIBCOMM_BUFFER_SIZE = 1024 * 8; - -gsocket gs_invalid_gsock = {0, 0, 0, 0}; - -static void gs_s_build_reply_conntion(libcommaddrinfo* addr_info, int remote_version); - -extern GlobalNodeDefinition* global_node_definition; - -extern knl_instance_context g_instance; - -/* - * function name : gs_change_capacity - * description : If GUC parameter "comm_max_datanode" changed this function will be called. - * notice : Only for postmaster thread. - * arguments : - * __in newval: new value (sum of CN and DN). - */ -void gs_change_capacity(int new_node_num) -{ - /* Only postmaster thread can change the g_expect_node_num, - * "g_cur_node_num==0" means postmaster doesn't finish initialization. - */ - if ((t_thrd.proc_cxt.MyProcPid != PostmasterPid) || - (new_node_num == g_instance.comm_cxt.counters_cxt.g_cur_node_num) || - (g_instance.comm_cxt.counters_cxt.g_cur_node_num == 0)) { - return; - } - - /* range for node_num (2,4096) */ - if ((new_node_num > MAX_CN_DN_NODE_NUM) || (new_node_num < MIN_CN_DN_NODE_NUM)) { - LIBCOMM_ELOG(WARNING, "(pm|change capacity)\tInvalidate node num: %d.", new_node_num); - return; - } - - g_instance.comm_cxt.counters_cxt.g_expect_node_num = new_node_num; - g_instance.comm_cxt.quota_cxt.g_quota_changing->post(); - LIBCOMM_ELOG(LOG, - "(pm|change capacity)\tg_cur_node_num [%d], g_expect_node_num [%d].", - g_instance.comm_cxt.counters_cxt.g_cur_node_num, - g_instance.comm_cxt.counters_cxt.g_expect_node_num); -} - -void comm_fill_hash_ctl(HASHCTL* ctl, Size k_size, Size e_size) -{ - ctl->keysize = k_size; - ctl->entrysize = e_size; - ctl->hash = tag_hash; - ctl->hcxt = g_instance.comm_cxt.comm_global_mem_cxt; - return; -} - -void gs_init_hash_table() -{ - AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); - HASHCTL tid_ctl, sock_ver_ctl, sock_id_ctl, nodename_ctl, ipstat_ctl; - int flags, rc; - - /* init g_htab_tid_poll */ - rc = memset_s(&tid_ctl, sizeof(tid_ctl), 0, sizeof(HASHCTL)); - securec_check(rc, "\0", "\0"); - - comm_fill_hash_ctl(&tid_ctl, sizeof(int), sizeof(tid_entry)); - flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX; - g_htab_tid_poll = hash_create("libcomm tid lookup hash", 65535, &tid_ctl, flags); - LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_tid_poll_lock, 0); - - /* init g_htab_socket_version */ - rc = memset_s(&sock_ver_ctl, sizeof(sock_ver_ctl), 0, sizeof(HASHCTL)); - securec_check(rc, "\0", "\0"); - - comm_fill_hash_ctl(&sock_ver_ctl, sizeof(int), sizeof(sock_ver_entry)); - flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX; - g_htab_socket_version = hash_create("libcomm socket version lookup hash", 65535, &sock_ver_ctl, flags); - LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_socket_version_lock, 0); - - /* init g_htab_socket_version */ - rc = memset_s(&sock_id_ctl, sizeof(sock_id_ctl), 0, sizeof(HASHCTL)); - securec_check(rc, "\0", "\0"); - - comm_fill_hash_ctl(&sock_id_ctl, sizeof(sock_id), sizeof(sock_id_entry)); - flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX; - g_htab_fd_id_node_idx = hash_create("libcomm socket & node_idx lookup hash", 65535, &sock_id_ctl, flags); - LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_fd_id_node_idx_lock, 0); - - /* init g_htab_nodename_node_idx */ - rc = memset_s(&nodename_ctl, sizeof(nodename_ctl), 0, sizeof(HASHCTL)); - securec_check(rc, "\0", "\0"); - - comm_fill_hash_ctl(&nodename_ctl, sizeof(char_key), sizeof(nodename_entry)); - flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX; - g_htab_nodename_node_idx = hash_create("libcomm nodename & node_idx lookup hash", 65535, &nodename_ctl, flags); - LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_nodename_node_idx_lock, 0); - - /* init g_htab_socket_version */ - rc = memset_s(&ipstat_ctl, sizeof(ipstat_ctl), 0, sizeof(HASHCTL)); - securec_check(rc, "\0", "\0"); - - comm_fill_hash_ctl(&ipstat_ctl, sizeof(ip_key), sizeof(ip_state_entry)); - flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX; - g_htab_ip_state = hash_create("libcomm ip & status lookup hash", 65535, &ipstat_ctl, flags); - LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_ip_state_lock, 0); -} - -void gs_set_hs_shm_data(HaShmemData* ha_shm_data) -{ - (void)atomic_set(&g_instance.comm_cxt.g_ha_shm_data, ha_shm_data); -} - -int gs_get_stream_num(void) -{ - return g_instance.comm_cxt.counters_cxt.g_max_stream_num; -} // gs_get_stream_num - -/* - * function name : gs_map_sock_id_to_node_idx - * description : save the key of fd_id and value of node idx into g_htab_fd_id_node_idx - * arguments : - * fd_id: struct of socket and socket id. - * idx: node idx. - * return value : - * 0: succeed. - * -1: save to htab failed. - */ -int gs_map_sock_id_to_node_idx(const sock_id fd_id, int idx) -{ -#ifdef LIBCOMM_FAULT_INJECTION_ENABLE - if (is_comm_fault_injection(LIBCOMM_FI_SOCKID_NODEIDX_FAILED)) { - errno = ECOMMTCPMEMALLOC; - LIBCOMM_ELOG(WARNING, - "(sock_id_to_node_idx)\t[FAULT INJECTION]Failed to save socket[%d,%d] for node[%d].", - fd_id.fd, - fd_id.id, - idx); - return -1; - } -#endif - - bool found = false; - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); - - struct sock_id_entry* entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &fd_id, HASH_ENTER, &found); - entry_id->entry.val = idx; - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); - - return 0; -} - -/* - * function name : gs_set_reply_sock - * description : set reply socket for g_r_node_sock by compare remote node name - * arguments : node_idx, node we want to set reply sock - */ -void gs_set_reply_sock(int node_idx) -{ - for (int recv_idx = 0; recv_idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; recv_idx++) { - if (strcmp(g_instance.comm_cxt.g_r_node_sock[recv_idx].remote_nodename, - g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename) == 0) { - // save reply socket in g_r_node_sock - g_instance.comm_cxt.g_r_node_sock[recv_idx].lock(); - g_instance.comm_cxt.g_r_node_sock[recv_idx].libcomm_reply_sock = - g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket; - g_instance.comm_cxt.g_r_node_sock[recv_idx].libcomm_reply_sock_id = - g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id; - g_instance.comm_cxt.g_r_node_sock[recv_idx].unlock(); - break; - } - } - - return; -} - -/* - * function name : gs_senders_struct_set - * description : initialize socket of local senders, for sending - * notice : retry 10 times inside if failure happens, - * arguments __in node_begin - * __in node_end - */ -void gs_senders_struct_set() -{ - int error = 0; - int i; - - // initialize sender socket and address storage - for (i = 0; i < MAX_CN_DN_NODE_NUM; i++) { - // g_s_node_sock - g_instance.comm_cxt.g_s_node_sock[i].init(); - - // initialize address storage - // initialize socket in gs_connect - error = mc_tcp_addr_init(g_instance.comm_cxt.localinfo_cxt.g_local_host, - 0, - &(g_instance.comm_cxt.g_senders->sender_conn[i].ss), - &(g_instance.comm_cxt.g_senders->sender_conn[i].ss_len)); - if (error != 0) { - ereport(FATAL, - (errmsg("(s|sender init)\tFailed to init sender[%d] for %s.", - i, - g_instance.comm_cxt.localinfo_cxt.g_local_host))); - } - - // set g_instance.comm_cxt.g_senders->sender_conn AND g_sender_count - LIBCOMM_PTHREAD_RWLOCK_INIT(&g_instance.comm_cxt.g_senders->sender_conn[i].rwlock, NULL); - g_instance.comm_cxt.g_senders->sender_conn[i].socket = -1; - g_instance.comm_cxt.g_senders->sender_conn[i].socket_id = -1; - g_instance.comm_cxt.g_senders->sender_conn[i].comm_bytes = 0; - g_instance.comm_cxt.g_senders->sender_conn[i].comm_count = 0; - } - - return; -} - -/* - * function name : gs_update_connection_state - * description : update the connections state of htab - * signal all threads block on the connections - * arguments : addr: structure of IP & PORT - * result: succeed or failed - */ -void gs_update_connection_state(ip_key addr, int result, bool is_signal, int node_idx) -{ - bool found = false; - - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_ip_state_lock); - ip_state_entry* entry_poll = (ip_state_entry*)hash_search(g_htab_ip_state, &addr, HASH_FIND, &found); - if (!found) { - LIBCOMM_ELOG(WARNING, "(s|connect)\tFail to get connection state:port[%s:%d].", addr.ip, addr.port); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); - Assert(found); - return; - } - - COMM_DEBUG_LOG( - "(s|gs_update_connection_state)\tchange connection [%s:%d] state to %d.", addr.ip, addr.port, result); - - /* cannot update connection state when the node idx mismatch */ - if (entry_poll->entry.val.node_idx != node_idx) { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); - return; - } - - entry_poll->entry.val.conn_state = result; - - if (is_signal) { - entry_poll->entry._signal_all(); - } - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); - return; -} - -/* - * function name : gs_s_get_connection_state - * description : gs_s_get_connection_state gives the - * connection state of indicated IP & PORT - * arguments : addr: structure of IP & PORT - * return value : - * CONNSTATECONNECTING: need to build a new connection - * CONNSTATEFAIL: get connection state failed - * CONNSTATESUCCEED: exist a valid connection - */ -int gs_s_get_connection_state(ip_key addr, int node_idx, int type) -{ - int rc = -1; - int old_slot_id = -1; - int retry_count = 0; - int state = CONNSTATEFAIL; - bool found = false; - - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_ip_state_lock); - ip_state_entry* entry_poll = (ip_state_entry*)hash_search(g_htab_ip_state, &addr, HASH_ENTER, &found); - - /* no connection exist before, add in htab and return need to create */ - if (unlikely(!found)) { - COMM_DEBUG_LOG("(s|gs_s_get_connection_state)\tno connection exist [%s:%d].", addr.ip, addr.port); - - entry_poll->entry.val.conn_state = CONNSTATECONNECTING; - entry_poll->entry.val.node_idx = node_idx; - entry_poll->entry._init(); - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); - return CONNSTATECONNECTING; - } - - switch (entry_poll->entry.val.conn_state) { - case CONNSTATECONNECTING: - /* someone is trying to make connetion, wait the result */ - COMM_DEBUG_LOG("(s|gs_s_get_connection_state)\twait for connection start [%s:%d].", addr.ip, addr.port); - - while (entry_poll->entry.val.conn_state == CONNSTATECONNECTING) { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); - rc = entry_poll->entry._timewait(CHECKCONNSTATTIMEOUT); - retry_count++; - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_ip_state_lock); - if (retry_count > (g_instance.comm_cxt.counters_cxt.g_comm_send_timeout / CHECKCONNSTATTIMEOUT)) { - errno = ECOMMTCPCONNTIMEOUT; - break; - } - } - COMM_DEBUG_LOG("(s|gs_s_get_connection_state)\twait for connection end [%s:%d]:%d.", - addr.ip, - addr.port, - entry_poll->entry.val.conn_state); - - /* connect state update became succeed, return CONNSTATESUCCEED */ - if (entry_poll->entry.val.conn_state == CONNSTATESUCCEED) { - state = CONNSTATESUCCEED; - } else { /* connect state is not succeed, return CONNSTATEFAIL */ - errno = (rc == ETIMEDOUT) ? ECOMMTCPCONNTIMEOUT : ECOMMTCPCONNFAIL; - state = CONNSTATEFAIL; - } - break; - - case CONNSTATEFAIL: - COMM_DEBUG_LOG( - "(s|gs_s_get_connection_state)\tconnection invalid, need to create[%s:%d].", addr.ip, addr.port); - - /* connection is failed before, update state to connecting and return need to create */ - entry_poll->entry.val.conn_state = CONNSTATECONNECTING; - entry_poll->entry.val.node_idx = node_idx; - state = CONNSTATECONNECTING; - break; - - case CONNSTATESUCCEED: - /* when the node idx mismatch with the valid connection before - * we assume it as a new connection, update node idx and close - * the old connection - */ - if (node_idx != entry_poll->entry.val.node_idx) { - old_slot_id = entry_poll->entry.val.node_idx; - entry_poll->entry.val.conn_state = CONNSTATECONNECTING; - entry_poll->entry.val.node_idx = node_idx; - state = CONNSTATECONNECTING; - } else { - /* a valid connection in htab, return connection succeed */ - state = CONNSTATESUCCEED; - } - break; - - default: - /* unexpected cases */ - LIBCOMM_ELOG(WARNING, - "(s|connect)\tUnexpected state in checking connection state:port[%s:%d], state:%d, node_idx:%d.", - addr.ip, - addr.port, - entry_poll->entry.val.conn_state, - entry_poll->entry.val.node_idx); - state = CONNSTATEFAIL; - break; - } - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); - - /* close old data connection */ - if (old_slot_id != -1) { - LIBCOMM_ELOG(WARNING, - "(s|connect)\tClose the old connections for node%d[%s]:port[%s:%d], type:%d.", - old_slot_id, - REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, old_slot_id), - addr.ip, - addr.port, - type); - if (type == DATA_CHANNEL) { - g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].ip_changed = true; - LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].rwlock); - struct sock_id libcomm_fd_id = {g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].socket, - g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].socket_id}; - gs_s_close_bad_data_socket(&libcomm_fd_id, ECOMMTCPPEERCHANGED, node_idx); - LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].rwlock); - } else { - g_instance.comm_cxt.g_s_node_sock[node_idx].ip_changed = true; - g_instance.comm_cxt.g_s_node_sock[node_idx].lock(); - struct sock_id ctrl_fd_id = {g_instance.comm_cxt.g_s_node_sock[old_slot_id].ctrl_tcp_sock, - g_instance.comm_cxt.g_s_node_sock[old_slot_id].ctrl_tcp_sock_id}; - gs_s_close_bad_ctrl_tcp_sock(&ctrl_fd_id, ECOMMTCPPEERCHANGED, false, node_idx); - g_instance.comm_cxt.g_s_node_sock[node_idx].unlock(); - } - } - - return state; -} - -/* - * add local thread id to g_htab_tid_poll - * then thread will call gs_poll during connecting, send and recv - */ -int gs_poll_create() -{ - struct tid_entry* entry_tid = NULL; - bool found = false; - - if (t_thrd.comm_cxt.libcomm_semaphore != NULL) { - return 0; - } - - if (g_htab_tid_poll == NULL) { - errno = ECOMMTCPCVINIT; - LIBCOMM_ELOG(WARNING, "(libcomm tid lookup hash)\tg_htab_tid_poll is NULL."); - return -1; - } - -#ifdef LIBCOMM_FAULT_INJECTION_ENABLE - if (is_comm_fault_injection(LIBCOMM_FI_CREATE_POLL_FAILED)) { - errno = ECOMMTCPMEMALLOC; - LIBCOMM_ELOG(WARNING, "(poll create)\t[FAULT INJECTION]Failed to add local tid to g_htab_tid_poll."); - return -1; - } -#endif - - if (t_thrd.comm_cxt.MyPid <= 0) { - t_thrd.comm_cxt.MyPid = gettid(); - } - - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_tid_poll_lock); - entry_tid = (tid_entry*)hash_search(g_htab_tid_poll, &t_thrd.comm_cxt.MyPid, HASH_ENTER, &found); - if (!found) { - entry_tid->entry.val = -1; - entry_tid->entry._init(); - } - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_tid_poll_lock); - t_thrd.comm_cxt.libcomm_semaphore = &(entry_tid->entry.sem); - - return 0; -} - -// delete tid from tid_poll, usually called when thread exit or logic conn is closed. -// but when the thread needed to delete is calling gs_poll, just signal it instead of del it. -// because for CN, thread usually wait for multiple logic connection, so we cannot del it when -// some logic connection is close. -void gs_poll_close() -{ - AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); - if (t_thrd.comm_cxt.libcomm_semaphore != NULL) { - t_thrd.comm_cxt.libcomm_semaphore = NULL; - - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_tid_poll_lock); - hash_search(g_htab_tid_poll, &t_thrd.comm_cxt.MyPid, HASH_REMOVE, NULL); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_tid_poll_lock); - } - - return; -} - -/* - * thread which call gs_poll and block in here - * until the expected event happened - * or some error happened(timeout, logic conn is closed, interruption happened). - */ -int gs_poll(int time_out) -{ - return t_thrd.comm_cxt.libcomm_semaphore->timed_wait(time_out); -} - -/* - * siganl thread when the expected event happened or some error happened - */ -void gs_poll_signal(binary_semaphore* sem) -{ - if (sem != NULL) { - sem->post(); - } -} - -/* - * when recv some interruption, gs_auxiliary will - * signal all thread waitting in gs_poll - * and threads will check interruption. - */ -void gs_broadcast_poll() -{ - HASH_SEQ_STATUS hash_seq; - tid_entry* element = NULL; - - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_tid_poll_lock); - - hash_seq_init(&hash_seq, g_htab_tid_poll); - - while ((element = (tid_entry*)hash_seq_search(&hash_seq)) != NULL) { - element->entry._signal(); - } - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_tid_poll_lock); -} - -/* - * function name : gs_get_node_idx - * description : gs_get_node_idx gives the node idx of backend. - * the first node connected to current process is node idx 0. - * arguments : - * node_name: name of backend - * len: NAMEDATALEN - * return value : -1:failed - * >0:node id - */ -int gs_get_node_idx(char* node_name) -{ -#ifdef LIBCOMM_FAULT_INJECTION_ENABLE - if (is_comm_fault_injection(LIBCOMM_FI_NO_NODEIDX)) { - errno = ECOMMTCPINVALNODEID; - LIBCOMM_ELOG(WARNING, "(s|get nodeid)\t[FAULT INJECTION]Failed to obtain node id for node %s.", node_name); - return -1; - } -#endif - // get node index - struct nodename_entry* entry_name = NULL; - struct char_key ckey; - bool found = false; - errno_t ss_rc; - int ret = -1; - uint32 cpylen = comm_get_cpylen(node_name, NAMEDATALEN); - ss_rc = memset_s(ckey.name, NAMEDATALEN, 0x0, NAMEDATALEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s(ckey.name, NAMEDATALEN, node_name, cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - ckey.name[cpylen] = '\0'; - - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_nodename_node_idx_lock); - entry_name = (nodename_entry*)hash_search(g_htab_nodename_node_idx, &ckey, HASH_ENTER, &found); - - if (found) { - ret = entry_name->entry.val; - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock); - return ret; - } - - // if the node is not registed, get a node index and save node name -> node index to hash table - int node_idx = nodename_count + 1; - if (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) { - hash_search(g_htab_nodename_node_idx, &ckey, HASH_REMOVE, NULL); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock); - errno = ECOMMTCPINVALNODEID; - return -1; - } - - nodename_count++; - entry_name->entry.val = node_idx; - ret = entry_name->entry.val; - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock); - LIBCOMM_ELOG(LOG, "(s|get idx)\tGenerate node idx [%d] for node:%s.", ret, node_name); - - return ret; -} - -/* - * function name : gs_get_stream_id - * description : producer get usable stream index for current query, which is designed by StreamKey. - * if the key is already in the hash table, return it! - * arguments : - * _in_ key_ns: libcomm stream key with node index. - * return value : -1:failed - * >0:stream id - */ -int gs_get_stream_id(int node_idx) -{ -#ifdef LIBCOMM_FAULT_INJECTION_ENABLE - if (is_comm_fault_injection(LIBCOMM_FI_NO_STREAMID)) { - errno = ECOMMTCPSTREAMIDX; - LIBCOMM_ELOG(WARNING, - "(s|get sid)\t[FAULT INJECTION]Failed to obtain stream for node[%d]:%s.", - node_idx, - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename); - return -1; - } -#endif - - int streamid = -1; - - // have no usable stream id - if (g_instance.comm_cxt.g_usable_streamid[node_idx].pop( - g_instance.comm_cxt.g_usable_streamid + node_idx, &streamid) <= 0) { - errno = ECOMMTCPSTREAMIDX; - LIBCOMM_ELOG(WARNING, - "(s|get sid)\tFailed to obtain stream for node[%d]:%s, usable:%d/%d.", - node_idx, - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, - g_instance.comm_cxt.g_usable_streamid[node_idx].count, - g_instance.comm_cxt.counters_cxt.g_max_stream_num); - return -1; - } - - // succeed to return the entry in g_s_htab_nodeid_skey_to_stream - COMM_DEBUG_LOG("(s|get sid)\tObtain stream[%d] for node[%d]:%s.", - streamid, - node_idx, - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename); - - return streamid; -} // gs_r_get_usable_streamid - -int gs_update_fd_to_htab_socket_version(struct sock_id* fd_id) -{ - struct sock_ver_entry* entry_ver = NULL; - bool found = false; - int fd = fd_id->fd; - int id = fd_id->id; - - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock); - - entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_ENTER, &found); - if (!found) { - entry_ver->entry.val = id; - } else { // if there is an entry already, we update the version(id) of the socket(fd) - entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1); - fd_id->id = entry_ver->entry.val; // set the new id into fd_id !!! - COMM_DEBUG_LOG("(add fd & version)\tSucceed to update socket[%d] version[%d].", fd, fd_id->id); - } - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); - - return 0; -} // gs_update_fd_to_htab_socket_version - -// receiver close all streams of a node which is designed by control tcp socket -// we call this function because of the broken control tcp connection or tcp connection, -// if it is tcp connection, we should send the close info to remote -// step 1: get node index (node_idx) -// step 2: traverse g_c_mailbox[node_idx][*] -// step 3: do notify and reset all cmailbox -static void gs_r_close_all_streams_by_fd_idx(int fd, int node_idx, int close_reason) -{ - struct c_mailbox* cmailbox = NULL; - struct FCMSG_T fcmsgs = {0x0}; - // Note: we should have locked at the caller, so we need not lock here again - // - LIBCOMM_ELOG(WARNING, - "(r|close all streams)\tTo reset all streams " - "by socket[%d] for node[%d]:%s, detail:%s.", - fd, - node_idx, - g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename, - mc_strerror(close_reason)); - - for (int j = 1; j < g_instance.comm_cxt.counters_cxt.g_max_stream_num; j++) { - cmailbox = &C_MAILBOX(node_idx, j); - LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); - - if ((fd == -1 || cmailbox->ctrl_tcp_sock == fd) && (cmailbox->state != MAIL_CLOSED)) { - gs_r_close_logic_connection(cmailbox, close_reason, &fcmsgs); - // reset local stream logic connection info - COMM_DEBUG_LOG("(r|close all streams)\tTo close stream[%d], node[%d]:%s, query[%lu], socket[%d].", - j, - node_idx, - g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename, - cmailbox->query_id, - cmailbox->ctrl_tcp_sock); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - // Send close ctrl msg to remote without cmailbox lock - if (IS_NOTIFY_REMOTE(close_reason)) { - (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[node_idx], &fcmsgs, ROLE_CONSUMER); - } - } else { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - } - } -} // gs_r_reset_all_streams_by_fd_idx - -// sender close all streams of a node which is designed by control tcp socket -// we call this function because of the broken control tcp connection, so we did not need to send the status to remote -// step 1: get node index (node_idx) -// step 2: traverse g_p_mailbox[node_idx][*] -// step 3: do notification and reset all pmailbox -static void gs_s_close_all_streams_by_fd_idx(int fd, int node_idx, int close_reason, bool with_ctrl_lock) -{ - struct p_mailbox* pmailbox = NULL; - struct FCMSG_T fcmsgs = {0x0}; - // Note: we should have locked at the caller, so we need not lock here again - // - LIBCOMM_ELOG(WARNING, - "(s|close all streams)\tTo reset all streams by socket[%d] for node[%d]:%s, detail:%s.", - fd, - node_idx, - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, - mc_strerror(close_reason)); - - for (int j = 1; j < g_instance.comm_cxt.counters_cxt.g_max_stream_num; j++) { - pmailbox = &P_MAILBOX(node_idx, j); - LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock); - - if (((pmailbox->ctrl_tcp_sock == -1) || (fd == -1) || (pmailbox->ctrl_tcp_sock == fd)) && - (pmailbox->state != MAIL_CLOSED)) { - COMM_DEBUG_LOG("(s|close all streams)\tTo close stream[%d], node[%d]:%s, query[%lu], socket[%d].", - j, - node_idx, - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, - pmailbox->query_id, - pmailbox->ctrl_tcp_sock); - - gs_s_close_logic_connection(pmailbox, close_reason, &fcmsgs); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); - // Send close ctrl msg to remote without cmailbox lock - if (IS_NOTIFY_REMOTE(close_reason)) { - if (with_ctrl_lock) { - (void)gs_send_ctrl_msg_without_lock( - &g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, node_idx, ROLE_PRODUCER); - } else { - (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, ROLE_PRODUCER); - } - } - } else { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); - } - } -} // gs_s_close_all_streams_by_ctrl_tcp_sock - -/* To remove the closed fd in the pooler list. - * for example, if we have many events in the poll_list [poll_1, poll_2, poll_3,...], the poller at the rear may be - * closed by the front one. So we need to check and delete the closed one in the poll_list. - * Notice: if the g_libcomm_poller_list doesn't belong to the Caller, it just returns. - */ -static void gs_clean_events(struct sock_id* old_fd_id) -{ - int fd = -1; - int id = -1; - - if (t_thrd.comm_cxt.g_libcomm_poller_list == NULL) { - return; - } - int nevents = t_thrd.comm_cxt.g_libcomm_poller_list->nevents; - for (int i = 0; i < nevents; i++) { - fd = (int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64 >> MC_POLLER_FD_ID_OFFSET)); - id = (int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64 & MC_POLLER_FD_ID_MASK)); - if ((old_fd_id->fd == fd) && (old_fd_id->id == id)) { - COMM_DEBUG_LOG("(clean events)\tClean socket[%d,%d] in the poller list.", fd, id); - - /* if the old_fd_id in the poller list, we need to remove it. - * To simplify, we just move the last one to this position. - * if "i" is the last one, i == nevents-1, it doesn't matter. - * if ievents[i] = - t_thrd.comm_cxt.g_libcomm_poller_list->events[nevents - 1]; - t_thrd.comm_cxt.g_libcomm_poller_list->nevents--; - break; - } - } - - return; -} - -// receiver close and clear bad tcp control socket, and related information -void gs_r_close_bad_ctrl_tcp_sock(struct sock_id* fd_id, int close_reason) -{ - int fd = fd_id->fd; - int id = fd_id->id; - bool found = false; - - if (fd < 0 || id < 0) { - return; - } - - LIBCOMM_PTHREAD_MUTEX_LOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock); - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock); - - // step1: remove the fd from the poller cabinet - // - if (g_instance.comm_cxt.pollers_cxt.g_r_poller_list->del_fd(fd_id) != 0) { - LIBCOMM_ELOG(WARNING, - " (r|close tcp socket)\tFailed to delete socket[%d,%d] from poll list:%s.", - fd, - id, - mc_strerror(errno)); - } - - // step2: get node index by fd - // - int node_idx = -1; - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); - sock_id_entry* entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_FIND, &found); - if (found) { - node_idx = entry_id->entry.val; - } - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); - // step3: make sure the fd and the version are matched, or it has been closed already - // - struct sock_ver_entry* entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_FIND, &found); - - if ((!found) || (entry_ver->entry.val != fd_id->id)) { - LIBCOMM_ELOG(WARNING, - "(r|close tcp socket)\tFailed to close socket[%d,%d], maybe already reused[%d,%d].", - fd, - id, - (found) ? fd : -1, - (found) ? entry_ver->entry.val : -1); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); - ; - LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock); - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); - hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); - return; - } - - LIBCOMM_ELOG(LOG, "(r|close bad tcp ctrl fds)\tClose bad socket with socket entry[%d,%d].", fd, id); - - if (node_idx >= 0) { - // step4: close all mailbox at receiver - // - gs_r_close_all_streams_by_fd_idx(fd_id->fd, node_idx, close_reason); - // step5: close the socket and reset the socket infomation structure(g_r_node_sock[node_idx]) - // - g_instance.comm_cxt.g_r_node_sock[node_idx].lock(); - if (g_instance.comm_cxt.g_r_node_sock[node_idx].ctrl_tcp_sock == fd_id->fd && - g_instance.comm_cxt.g_r_node_sock[node_idx].ctrl_tcp_sock_id == fd_id->id) { - g_instance.comm_cxt.g_r_node_sock[node_idx].close_socket_nl(CTRL_TCP_SOCK); - - LIBCOMM_ELOG(WARNING, - "(r|close tcp socket)\tTCP disconnect with socket[%d,%d] to host:%s, node[%d]:[%s].", - fd, - id, - g_instance.comm_cxt.g_r_node_sock[node_idx].remote_host, - node_idx, - g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename); - } - g_instance.comm_cxt.g_r_node_sock[node_idx].unlock(); - - gs_clean_events(fd_id); - } else { - mc_tcp_close(fd_id->fd); - } - // step6: if the fd is closed, we update the fd version - // - entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); - ; - LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock); - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); - hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); - - return; -} // gs_r_close_bad_ctrl_tcp_sock - -// sender close and clear bad tcp control socket, and related information -// clean_epoll is true when this function is called by sender flow ctrl thread, -// we delete fd from epoll list and close fd. -// clean_epoll is false when this function is called by producer thread, -// in this case, we cannot close fd and delete from epoll list, -// cause other thread may use this fd after close, -// while sender flow control thread still use this fd to recv. -// NOTE: fd can be closed and deleted from epoll list only under the sender flow ctrl. -void gs_s_close_bad_ctrl_tcp_sock(struct sock_id* fd_id, int close_reason, bool clean_epoll, int node_idx) -{ - int fd = fd_id->fd; - int id = fd_id->id; - ip_key addr; - bool is_addr = false; - errno_t ss_rc; - uint32 cpylen; - bool found = false; - - if (fd < 0 || id < 0) { - return; - } - - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock); - // step1: remove the fd from the poller cabinet - // - if (clean_epoll) { - gs_clean_events(fd_id); - if (g_instance.comm_cxt.pollers_cxt.g_s_poller_list->del_fd(fd_id) != 0) { - COMM_DEBUG_LOG("(s|cls bad tcp socket)\tFailed to remove bad socket with socket entry[%d,%d]:%s.", - fd, - id, - mc_strerror(errno)); - } - } - - // step2: make sure the fd and the version are matched, or it has been closed already - // - struct sock_ver_entry* entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_FIND, &found); - if (!found || entry_ver->entry.val != fd_id->id) { - LIBCOMM_ELOG(WARNING, - "(s|cls bad tcp socket)\tFailed to close bad socket[%d,%d], socket entry[%d,%d].", - fd, - id, - (found) ? fd : -1, - (found) ? entry_ver->entry.val : -1); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); - return; - } - - // step3: close all mailbox at receiver - // - if (node_idx >= 0) { - gs_s_close_all_streams_by_fd_idx(fd_id->fd, node_idx, close_reason, true); - } - - // step4: close the socket and reset the socket infomation structure(g_s_node_sock[node_idx]) - // - LIBCOMM_ELOG(LOG, - "(s|close bad tcp ctrl fds)\tClose bad socket with socket entry[%d,%d] : %s.", - fd, - id, - mc_strerror(close_reason)); - if (node_idx >= 0) { - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); - hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); - - if (g_instance.comm_cxt.g_s_node_sock[node_idx].ctrl_tcp_sock == fd_id->fd && - g_instance.comm_cxt.g_s_node_sock[node_idx].ctrl_tcp_sock_id == fd_id->id) { - cpylen = comm_get_cpylen(g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, HOST_LEN_OF_HTAB); - ss_rc = memset_s(addr.ip, HOST_LEN_OF_HTAB, 0x0, HOST_LEN_OF_HTAB); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s( - addr.ip, HOST_LEN_OF_HTAB, g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - addr.ip[cpylen] = '\0'; - - addr.port = g_instance.comm_cxt.g_s_node_sock[node_idx].ctrl_tcp_port; - is_addr = true; - // producer thread detect destination ip is changed - // then notify the origination backend to close connection - if (close_reason == ECOMMTCPPEERCHANGED) { - struct FCMSG_T fcmsgs = {0x0}; - fcmsgs.type = CTRL_PEER_CHANGED; - fcmsgs.node_idx = node_idx; - fcmsgs.streamid = 1; - - cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); - ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s( - fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - fcmsgs.nodename[cpylen] = '\0'; - - (void)gs_send_ctrl_msg_without_lock( - &g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, node_idx, ROLE_PRODUCER); - } - g_instance.comm_cxt.g_s_node_sock[node_idx].set_nl(-1, CTRL_TCP_SOCK); - g_instance.comm_cxt.g_s_node_sock[node_idx].set_nl(-1, CTRL_TCP_SOCK_ID); - LIBCOMM_ELOG(WARNING, - "(s|cls bad tcp socket)\tClose bad socket[%d,%d] for host:%s, node[%d]:%s.", - fd, - id, - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, - node_idx, - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename); - } - } - - // clean_epoll is true only under sender flow control thread - if (clean_epoll) { - mc_tcp_close(fd_id->fd); - // step5: if the fd is closed, we update the fd version - // - entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1); - } - // step6: update connection state in htab - // - if (is_addr) { - gs_update_connection_state(addr, CONNSTATEFAIL, false, node_idx); - } - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); - -} // gs_s_close_bad_ctrl_tcp_sock - -int gs_memory_pool_queue_initial_success(uint32 index) -{ - return g_memory_pool_queue.initialize(index); -} - -struct mc_lqueue_item* gs_memory_pool_queue_pop(char* iov) -{ - return (struct mc_lqueue_item*)g_memory_pool_queue.pop(iov); -} - -bool gs_memory_pool_queue_push(char* item) -{ - return g_memory_pool_queue.push(item); -} - -// Calculation how many quota size need to add in mailbox -static long gs_add_quota_size(c_mailbox* cmailbox) -{ -#define COMM_HIGH_MEM_USED (used_memory >= (g_instance.comm_cxt.commutil_cxt.g_total_usable_memory * 0.8)) -#define COMM_MIDDLE_MEM_USED \ - (used_memory > (g_instance.comm_cxt.commutil_cxt.g_total_usable_memory * 0.5) && \ - used_memory < (g_instance.comm_cxt.commutil_cxt.g_total_usable_memory * 0.8)) -#define COMM_LOW_MEM_USED (used_memory <= (g_instance.comm_cxt.commutil_cxt.g_total_usable_memory * 0.5)) - - long used_memory = gs_get_comm_used_memory(); - long add_quota = 0; - long max_buff = 0; // must be equal [DEFULTMSGLEN, comm_quota_size] - long buff_used = cmailbox->buff_q->u_size; // the used buffer size in this mailbox - long old_quota = cmailbox->bufCAP; // the quota size in this mailbox - - used_memory -= g_memory_pool_queue.size() * IOV_ITEM_SIZE; - - // Calculate the maximum buffer size for this mailbox - if (COMM_HIGH_MEM_USED) { - max_buff = DEFULTMSGLEN; - } else if (COMM_LOW_MEM_USED) { - max_buff = (g_instance.comm_cxt.quota_cxt.g_quota > DEFULTMSGLEN) ? g_instance.comm_cxt.quota_cxt.g_quota - : DEFULTMSGLEN; - } else { // COMM_MIDDLE_MEM_USED - max_buff = (g_instance.comm_cxt.quota_cxt.g_quota / 8 > DEFULTMSGLEN) - ? g_instance.comm_cxt.quota_cxt.g_quota / 8 - : DEFULTMSGLEN; - } - - // because: max_buff = buff_used + old_quota + add_quota - // so: add_quota = max_buff - buff_used - old_quota - add_quota = max_buff - buff_used - old_quota; - - /* - * buff_used+old_quota is total data size that can be received when no send quota. - * if (buff_used+old_quota < g_quota/2), need send quota. - * if (buff_used+old_quota < DEFULTMSGLEN), need send quota. - */ - if ((buff_used + old_quota < (long)(g_instance.comm_cxt.quota_cxt.g_quota >> 1)) || - ((unsigned long)(buff_used + old_quota) < DEFULTMSGLEN)) { - return add_quota < 0 ? 0 : add_quota; - } else { - return 0; - } -} - -// auxiliary thread use it to change the stream state and send control message to remote point (sender) -bool gs_r_quota_notify(c_mailbox* cmailbox, FCMSG_T* msg) -{ - errno_t ss_rc; - uint32 cpylen; - int node_idx = cmailbox->idx; - int streamid = cmailbox->streamid; - unsigned long add_quota = gs_add_quota_size(cmailbox); - - if (add_quota > 0) { - // change local stream state and quota first - cmailbox->bufCAP += add_quota; - cmailbox->state = MAIL_RUN; - - COMM_DEBUG_LOG("(r|quota notify)\tSend quota to node[%d]:%s on stream[%d].", - node_idx, - g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename, - streamid); - - // send resume message to change remote stream state and quota - msg->type = CTRL_ADD_QUOTA; - msg->node_idx = cmailbox->idx; - msg->streamid = cmailbox->streamid; - msg->streamcap = add_quota; - msg->version = cmailbox->remote_version; - msg->query_id = cmailbox->query_id; - - cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); - ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - msg->nodename[cpylen] = '\0'; - - return true; - } - - return false; -} // gs_r_quota_notify - -// traverse all the c_mailbox(es) to find the first query who used memory, -// and make it failure to release the memory. Otherwise, the communication layer maybe hang up. -void gs_r_release_comm_memory() -{ - uint64 release_query_id = 0; - int nid = 0; - int sid = 1; - struct c_mailbox* cmailbox = NULL; - unsigned long buff_size = 0; - unsigned long total_buff_size = 0; - struct FCMSG_T fcmsgs = {0x0}; - - for (nid = 0; nid < g_instance.comm_cxt.counters_cxt.g_cur_node_num; nid++) { - for (sid = 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { - cmailbox = &C_MAILBOX(nid, sid); - LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); - - if (cmailbox->buff_q->u_size <= 0) { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - continue; - } - - // find the first query to release memory, save query id - if (release_query_id == 0) { - release_query_id = cmailbox->query_id; - } - - if (cmailbox->query_id == release_query_id) { - buff_size = cmailbox->buff_q->u_size; - total_buff_size += buff_size; - COMM_DEBUG_LOG("(r|release memory)\tReset stream[%d] on node[%d]:%s " - "for query[%lu] to release memory[%lu Byte].", - sid, - nid, - REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, nid), - release_query_id, - buff_size); - - gs_r_close_logic_connection(cmailbox, ECOMMTCPRELEASEMEM, &fcmsgs); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[nid], &fcmsgs, ROLE_CONSUMER); - } else { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - } - } - } - - LIBCOMM_ELOG(WARNING, - "(r|release memory)\tReset query[%lu] to release memory[%lu Byte].", - release_query_id, - total_buff_size); -} // gs_r_release_comm_memory - -// if we failed to receive message from a tcp listen socket, we should do following things -// step1: reset the streams of the related node -// step2: delete it from epoll cabinet -// step3: update the socket version -// step4: delete the socket from hash table socke -> node index (g_r_htab_data_socket_node_idx) -// step5: close the old tcp socket -void gs_r_close_bad_data_socket(int node_idx, sock_id fd_id, int close_reason, bool is_lock) -{ - if (node_idx >= 0) { - gs_r_close_all_streams_by_fd_idx(-1, node_idx, ECOMMTCPDISCONNECT); - if (is_lock) { - LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].rwlock); - } - g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].socket = -1; - if (is_lock) { - LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].rwlock); - } - } - - bool found = false; - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock); - struct sock_ver_entry* entry_ver = - (sock_ver_entry*)hash_search(g_htab_socket_version, &fd_id.fd, HASH_FIND, &found); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); - - if (!found || entry_ver->entry.val != fd_id.id) { - LIBCOMM_ELOG(WARNING, - "(r|close bad data socket)\tFailed to close bad socket[%d,%d], socket entry[%d,%d].", - fd_id.fd, - fd_id.id, - (found) ? fd_id.fd : -1, - (found) ? entry_ver->entry.val : -1); - return; - } - - bool is_delete = false; - - LIBCOMM_PTHREAD_MUTEX_LOCK(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock); - - /* try to delete old_fd in g_libcomm_receiver_poller_list. - * because we have several recv thread, if the old_fd_id belongs to this thread, it can delete it successfully, - * otherwise, it returns false. - */ - if (t_thrd.comm_cxt.g_libcomm_recv_poller_hndl_list != NULL) { - is_delete = (t_thrd.comm_cxt.g_libcomm_recv_poller_hndl_list->del_fd(&fd_id) == 0) ? true : false; - } - LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock); - - /* del fd_id in the htab, - * next time, -1 = g_htab_fd_id_node_idx.get_value(fd_id), So we needn't to gs_r_close_all_streams again. - */ - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); - hash_search(g_htab_fd_id_node_idx, &fd_id, HASH_REMOVE, NULL); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); - - if (is_delete) { - // if the old_fd_id belongs to this recv thread, we need to clean it in the poll list, and then close(fd). - gs_clean_events(&fd_id); - if (gs_update_fd_to_htab_socket_version(&fd_id) < 0) { - LIBCOMM_ELOG( - WARNING, "(r|close bad data socket)\tFailed to update bad data socket[%d,%d].", fd_id.fd, fd_id.id); - } - mc_tcp_close(fd_id.fd); - } else { - /* if the old_fd_id belongs to other recv thread, it means, the old_fd_id isn't in this poll_list, - * So we needn't to gs_clean_events(). we just use shutdown to send notification signal. - */ - shutdown(fd_id.fd, SHUT_RDWR); - COMM_DEBUG_LOG("(r|close bad data socket)\tSend shutdown signal for [%d,%d].", fd_id.fd, fd_id.id); - } -} - -// if we failed to send message to the destination, we should do following things -void gs_s_close_bad_data_socket(struct sock_id* fd_id, int close_reason, int node_idx) -{ - errno_t ss_rc; - uint32 cpylen; - int fd = fd_id->fd; - int id = fd_id->id; - ip_key addr; - bool is_addr = false; - bool found = false; - - if ((fd_id->fd < 0) || (fd_id->id < 0)) { - return; - } - - // step1: make sure the fd and the version are matched, or it has been closed already - // - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock); - struct sock_ver_entry* entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_FIND, &found); - - if (!found) { - mc_tcp_close(fd_id->fd); - } - - if (!found || entry_ver->entry.val != fd_id->id) { - LIBCOMM_ELOG(WARNING, - "(s|cls bad data socket)\tFailed to close bad socket[%d,%d], socket entry[%d,%d].", - fd, - id, - (found) ? fd : -1, - (found) ? entry_ver->entry.val : -1); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); - hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); - - return; - } - - // step2: close the bad socket - // - LIBCOMM_ELOG(LOG, "(s|cls bad data socket)\tClose bad socket with socket entry[%d,%d].", fd, id); - - if (node_idx >= 0) { - /* reset the socket for sender, unexpected case if this condition mismatch */ - if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket == fd_id->fd && - g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id == fd_id->id) { - - cpylen = - comm_get_cpylen(g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host, HOST_LEN_OF_HTAB); - ss_rc = memset_s(addr.ip, HOST_LEN_OF_HTAB, 0x0, HOST_LEN_OF_HTAB); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s(addr.ip, - HOST_LEN_OF_HTAB, - g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host, - cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - addr.ip[cpylen] = '\0'; - - addr.port = g_instance.comm_cxt.g_senders->sender_conn[node_idx].port; - is_addr = true; - - mc_tcp_close(g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket); - g_instance.comm_cxt.g_senders->sender_conn[node_idx].port = -1; - g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket = -1; - g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id = -1; - g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id = 0; - LIBCOMM_ELOG(WARNING, - "(s|cls bad data socket)\tClose bad data socket with socket entry[%d,%d] " - "to host:%s, node[%d], node name[%s]:%s.", - fd, - id, - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, - node_idx, - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, - mc_strerror(errno)); - } - } else { - mc_tcp_close(fd_id->fd); - } - // step3: update connection state in htab - // - if (is_addr) { - gs_update_connection_state(addr, CONNSTATEFAIL, false, node_idx); - } - - // step4: if the bad socket is closed, we update the fd version - // - entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); - - /* - * close all p_mailbox[node_idx][*] - * without g_htab_socket_version lock - * with g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock - */ - if (node_idx >= 0) { - gs_s_close_all_streams_by_fd_idx(-1, node_idx, close_reason, false); - } - - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); - hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); - - return; -} // gs_s_close_bad_data_socket - -/* - * @Description: push the data package to cmailbox buffer. - * @IN cmailbox: point of cmailbox. - * @IN iov: data package. - * @Return: -1: push data failed. - * 0: push data succsessed. - * @See also: - */ -int gs_push_cmailbox_buffer(c_mailbox* cmailbox, struct mc_lqueue_item* q_item, int version) -{ - struct iovec* iov = q_item->element.data; - COMM_TIMER_INIT(); - - int sid = cmailbox->streamid; - int idx = cmailbox->idx; - uint64 signal_start = 0; - uint64 signal_end = 0; - uint64 time_now = 0; - - LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); - - // if the stream is closed or ready to close, the data should be dropped. - if (false == gs_check_mailbox(cmailbox->local_version, version)) { - COMM_DEBUG_LOG("(r|inner recv)\tStream[%d] is closed for node[%d]:%s, drop reveived message[%d].", - sid, - idx, - g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename, - (int)iov->iov_len); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - errno = cmailbox->close_reason; - return -1; - } - - DEBUG_QUERY_ID = cmailbox->query_id; - - // there is buffer/quota to process the received data - if (cmailbox->bufCAP >= (unsigned long)(iov->iov_len)) { - COMM_DEBUG_LOG("(r|inner recv)\tNode[%d]:%s stream[%d] recv %zu msg:%c, bufCAP[%lu] and buff_q->u_size[%lu].", - idx, - g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename, - sid, - iov->iov_len, - ((char*)iov->iov_base)[0], - cmailbox->bufCAP, - cmailbox->buff_q->u_size); - - // put the message to the buffer in the c_mailbox - (void)mc_lqueue_add(cmailbox->buff_q, q_item); - - if (g_instance.comm_cxt.quota_cxt.g_having_quota) { - cmailbox->bufCAP -= iov->iov_len; - } - - signal_start = COMM_STAT_TIME(); - // wake up the Consumer thread of executor, to notify Consumer of arriving new message - gs_poll_signal(cmailbox->semaphore); - - COMM_TIMER_LOG("(r|inner recv)\tCache data from node[%d]:%s stream[%d].", - idx, - g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename, - sid); - } else { // there is no buffer/quota to process the received data, it should not happen - LIBCOMM_ELOG(WARNING, - "(r|inner recv)\tNode[%d] stream[%d], node name[%s] has bufCAP[%lu] and got[%d].", - idx, - sid, - g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename, - cmailbox->bufCAP, - (int)iov->iov_len); - LIBCOMM_ASSERT(false, idx, sid, ROLE_CONSUMER); - } - - /* update the statistic information of the mailbox */ - if (cmailbox->statistic != NULL) { - time_now = COMM_STAT_TIME(); - if (cmailbox->statistic->first_recv_time == 0) { - cmailbox->statistic->first_recv_time = time_now; - } - signal_end = time_now; - cmailbox->statistic->total_signal_time += ABS_SUB(signal_end, signal_start); - cmailbox->statistic->last_recv_time = time_now; - cmailbox->statistic->recv_bytes += iov->iov_len; - cmailbox->statistic->recv_loop_time += ABS_SUB(time_now, t_thrd.comm_cxt.g_receiver_loop_poll_up); - cmailbox->statistic->recv_loop_count++; - } - - if (cmailbox->bufCAP < DEFULTMSGLEN) { - cmailbox->state = MAIL_HOLD; - } - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - - return 0; -} - -int gs_handle_data_delay_message(int idx, struct mc_lqueue_item* q_item, uint16 msg_type) -{ - struct c_mailbox* cmailbox = NULL; - struct libcomm_delay_package* delay_msg = NULL; - struct iovec* iov = q_item->element.data; - - if (idx < 0) { - return -1; - } - - delay_msg = (struct libcomm_delay_package*)iov->iov_base; - - if (msg_type == LIBCOMM_PKG_TYPE_DELAY_REQUEST) { - delay_msg->recv_time = (uint32)mc_timers_us(); - } else if (msg_type == LIBCOMM_PKG_TYPE_DELAY_REPLY) { - delay_msg->finish_time = (uint32)mc_timers_us(); - } - - // put the message to the buffer in the c_mailbox - cmailbox = &C_MAILBOX(idx, 0); - LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); - (void)mc_lqueue_add(cmailbox->buff_q, q_item); - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - - return 0; -} - -/* - * function name : gs_s_close_logic_connection - * description : producer close logic connetion and reset mailbox, - * if producer call this, we only set state is CTRL_TO_CLOSE, - * then really close when consumer send MAIL_CLOSED message. - * notice : we must get mailbox lock before - * arguments : _in_ cmailbox: libcomm logic conntion info. - * _in_ close_reason: close reason. - */ -void gs_s_close_logic_connection(struct p_mailbox* pmailbox, int close_reason, FCMSG_T* msg) -{ - errno_t ss_rc; - uint32 cpylen; - - if (pmailbox->state == MAIL_CLOSED) { - return; - } - - // when sender closes pmailbox, if close reason != remote close, and the state of pmailbox is MAIL_READY, - // we set pmailbox to MAIL_TO_CLOSE, then wait for top consumer(gs_connect()) to close it. - if ((pmailbox->state == MAIL_READY) && (close_reason != ECOMMTCPREMOETECLOSE)) { - pmailbox->state = MAIL_TO_CLOSE; - // wake up the producer who is waiting - gs_poll_signal(pmailbox->semaphore); - pmailbox->semaphore = NULL; - return; - } - - // 1, if tcp disconnect, we can not send control message on tcp channel, - // remote can receive disconnect event when flow control thread call epoll_wait. - // 2, close reason is ECOMMTCPREMOETECLOSE means remote send MAIL_CLOSED, - // we could not reply MAIL_CLOSED message. - if (IS_NOTIFY_REMOTE(close_reason) && msg) { - msg->type = CTRL_CLOSED; - msg->node_idx = pmailbox->idx; - msg->streamid = pmailbox->streamid; - msg->streamcap = 0; - msg->version = pmailbox->remote_version; - msg->query_id = pmailbox->query_id; - - cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); - ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - msg->nodename[cpylen] = '\0'; - } - - // wake up the producer who is waiting - gs_poll_signal(pmailbox->semaphore); - - // At last, reset mailbox and clean hash table - gs_s_reset_pmailbox(pmailbox, close_reason); - - return; -} - -// send assert fail msg via ctrl connection if debug mode enable and assert failed -// -void gs_libcomm_handle_assert(bool condition, int nidx, int sidx, int node_role) -{ - errno_t ss_rc; - uint32 cpylen; - - if (mc_unlikely(!condition)) { - struct FCMSG_T fcmsgs = {0x0}; - // notify peer assertion failed - fcmsgs.type = CTRL_ASSERT_FAIL; - fcmsgs.node_idx = nidx; - fcmsgs.streamid = sidx; - - cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); - ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s(fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - fcmsgs.nodename[cpylen] = '\0'; - - if (node_role == ROLE_PRODUCER) { - (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[nidx], &fcmsgs, node_role); - struct p_mailbox* pmailbox = NULL; - pmailbox = &(P_MAILBOX(nidx, sidx)); - LIBCOMM_ELOG(WARNING, - "(s|handle assert)\tNode[%d] stream[%d] assert fail, node name[%s] with state[%d] has bufCAP[%lu].", - nidx, - sidx, - g_instance.comm_cxt.g_s_node_sock[nidx].remote_nodename, - pmailbox->state, - pmailbox->bufCAP); - MAILBOX_ELOG(pmailbox, WARNING, "(s|handle assert)\tMailbox Info which assert fail."); - } else { - (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[nidx], &fcmsgs, node_role); - struct c_mailbox* cmailbox = NULL; - cmailbox = &(C_MAILBOX(nidx, sidx)); - LIBCOMM_ELOG(WARNING, - "(r|handle assert)\tNode[%d] stream[%d] assert fail, node name[%s] with state[%d] has bufCAP[%lu] and " - "buff_q->u_size[%lu].", - nidx, - sidx, - g_instance.comm_cxt.g_r_node_sock[nidx].remote_nodename, - cmailbox->state, - cmailbox->bufCAP, - cmailbox->buff_q->u_size); - MAILBOX_ELOG(cmailbox, WARNING, "(r|handle assert)\tMailbox Info which assert fail."); - } - Assert(condition); - } -} - -/* - * function name : gs_s_build_reply_conntion - * description : as the connection between cn & dn is duplex. - * cn need to inital cmailbox to recv msgs from dn - * arguments : fcmsgr: provides gs_sock - */ -static void gs_s_build_reply_conntion(libcommaddrinfo* addr_info, int remote_version) -{ - int node_idx = addr_info->gs_sock.idx; - int streamid = addr_info->gs_sock.sid; - int local_version = addr_info->gs_sock.ver; - - // initialize consumer cmailbox - struct c_mailbox* cmailbox = &C_MAILBOX(node_idx, streamid); - LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); - - if (gs_check_mailbox(cmailbox->local_version, local_version) == true) { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - return; - } - - if (cmailbox->state != MAIL_CLOSED) { - MAILBOX_ELOG(cmailbox, - WARNING, - "(s|build reply conn)\tFailed to get mailbox for node[%d,%d]:%s.", - node_idx, - streamid, - REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx)); - gs_r_close_logic_connection(cmailbox, ECOMMTCPREMOETECLOSE, NULL); - } - - cmailbox->local_version = local_version; - cmailbox->remote_version = remote_version; - cmailbox->ctrl_tcp_sock = g_instance.comm_cxt.g_r_node_sock[node_idx].ctrl_tcp_sock; - cmailbox->state = MAIL_RUN; - cmailbox->bufCAP = DEFULTMSGLEN; - cmailbox->stream_key = addr_info->streamKey; - cmailbox->query_id = DEBUG_QUERY_ID; - cmailbox->local_thread_id = 0; - cmailbox->peer_thread_id = 0; - cmailbox->close_reason = 0; - if (g_instance.comm_cxt.commutil_cxt.g_stat_mode && (cmailbox->statistic == NULL)) { - LIBCOMM_MALLOC(cmailbox->statistic, sizeof(struct cmailbox_statistic), cmailbox_statistic); - if (NULL == cmailbox->statistic) { - errno = ECOMMTCPRELEASEMEM; - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - return; - } - } - COMM_STAT_CALL(cmailbox, cmailbox->statistic->start_time = (uint32)mc_timers_ms()); - COMM_DEBUG_LOG("(s|build reply conn)\tNode[%d] stream[%d], node name[%s] is in state[%s].", - node_idx, - streamid, - g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename, - stream_stat_string(cmailbox->state)); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - - return; -} - -/* - * check all mailboxs state is MAIL_READY - * if the state of mailbox is MAIL_TO_CLOSE, top consumer will close the logic connection. - * if mailbox state is CTRL_CLOSE(gs_check_mailbox return false), - * means consumer do not need the data from this datanode, not report error - */ -int gs_check_all_mailbox(libcommaddrinfo** libcomm_addrinfo, int addr_num, int re, - bool TempImmediateInterruptOK, int timeout) -{ - int wait_index; - int i; - int node_idx = -1; - int streamid = -1; - int version = -1; - int remote_version = -1; - int error_index = -1; - bool build_reply_conn = false; - libcommaddrinfo* addr_info = NULL; - struct p_mailbox* pmailbox = NULL; - - for (;;) { - wait_index = -1; - for (i = 0; i < addr_num; i++) { - addr_info = libcomm_addrinfo[i]; - node_idx = addr_info->gs_sock.idx; - streamid = addr_info->gs_sock.sid; - version = addr_info->gs_sock.ver; - build_reply_conn = false; - - pmailbox = &P_MAILBOX(node_idx, streamid); - LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock); - // if gs_check_mailbox return false, means consumer close it, not need report error - if (gs_check_mailbox(pmailbox->local_version, version) == true) { - if (pmailbox->state == MAIL_READY) { - pmailbox->semaphore = t_thrd.comm_cxt.libcomm_semaphore; - COMM_DEBUG_LOG( - "(s|parallel connect)\tWait node[%d] stream[%d] state[%s], node name[%s], bufCAP[%lu].", - node_idx, - streamid, - stream_stat_string(pmailbox->state), - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, - pmailbox->bufCAP); - wait_index = i; - } else if (pmailbox->state == MAIL_TO_CLOSE) { // mail would close later, sender closes pmailbox, - LIBCOMM_ELOG(WARNING, - "(s|parallel connect)\tMAIL_TO_CLOSE node[%d] stream[%d] state[%s], node name[%s].", - node_idx, - streamid, - stream_stat_string(pmailbox->state), - g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename); - gs_s_close_logic_connection(pmailbox, ECOMMTCPREMOETECLOSE, NULL); - - // before continue or goto clean_connection, we must release the sinfo_lock. - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); - if (IS_PGXC_COORDINATOR) { - addr_info->gs_sock = GS_INVALID_GSOCK; - continue; - } else { - errno = ECOMMTCPCONNFAIL; - error_index = i; - return gs_clean_connection(libcomm_addrinfo, addr_num, error_index, - re, TempImmediateInterruptOK); - } - } else { - pmailbox->semaphore = NULL; - // for cn initial cmailbox as well as the connection is duplex - if (IS_PGXC_COORDINATOR) { - remote_version = pmailbox->remote_version; - build_reply_conn = true; - } - } - } else { - // if gs_check_mailbox return false, means consumer close it, we need to reset gsockt - addr_info->gs_sock = GS_INVALID_GSOCK; - } - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); - - // for cn initial cmailbox as well as the connection is duplex - if (build_reply_conn) { - // build cmailbox with the same version and remote_verion as pmailbox, - // when this connection is duplex. - gs_s_build_reply_conntion(addr_info, remote_version); - } - } - - // we wait on the last mailbox that state is not MAIL_READY - if (wait_index >= 0) { - pgstat_report_waitstatus_comm(STATE_STREAM_WAIT_CONNECT_NODES, - libcomm_addrinfo[wait_index]->nodeIdx, - wait_index + 1, - -1, - global_node_definition ? global_node_definition->num_nodes : -1); - - re = gs_poll(timeout); - if (re == ETIMEDOUT) { - if (IS_PGXC_COORDINATOR) { - /* close all timeout connections */ - gs_close_timeout_connections(libcomm_addrinfo, addr_num, node_idx, streamid); - } else { - errno = ETIMEDOUT; - error_index = wait_index; - return gs_clean_connection(libcomm_addrinfo, addr_num, error_index, - re, TempImmediateInterruptOK); - } - } - } else { - break; - } - } - - return 0; -} - -/* - * function name : gs_s_send_start_ctrl_msg - * description : producer send local thread id to consumer, - * it is means producer start to send data. - * notice : we must get mailbox lock before. - * arguments : - * _in_ pmailbox: logic conntion info. - * return value : - * false: failed. - * true : succeed. - */ -bool gs_s_form_start_ctrl_msg(p_mailbox* pmailbox, FCMSG_T* msg) -{ - pid_t local_tid = t_thrd.comm_cxt.MyPid; - errno_t ss_rc; - uint32 cpylen; - - if (pmailbox->query_id != DEBUG_QUERY_ID) { - pmailbox->query_id = DEBUG_QUERY_ID; - } - - // send local thread id to remote - if (local_tid != pmailbox->local_thread_id) { - int node_idx = pmailbox->idx; - int streamid = pmailbox->streamid; - - // change local stream state and quota first - pmailbox->local_thread_id = local_tid; - - // change local stream state and quota first - msg->type = CTRL_PEER_TID; - msg->node_idx = node_idx; - msg->streamid = streamid; - msg->streamcap = 0; - msg->version = pmailbox->remote_version; - msg->extra_info = pmailbox->local_thread_id; - msg->query_id = pmailbox->query_id; - - cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); - ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - msg->nodename[cpylen] = '\0'; - - return true; - } - - return false; -} // gs_s_send_start_ctrl_msg - -/* - * @Description: push the data package to local cmailbox buffer. - * @IN streamid: the producer and consumer have the same stream id. - * @IN message: data message. - * @IN m_len: message len. - * @Return: -1: push data failed. - * m_len: push data succsessed. - * @See also: local producer can use memcpy to push data package, - * no need push to data stack - */ -int gs_push_local_buffer(int streamid, const char* message, int m_len, int cmailbox_version) -{ - int cmailbox_idx = -1; - struct char_key ckey; - c_mailbox* cmailbox = NULL; - errno_t ss_rc; - bool found = false; - uint32 cpylen; - - t_thrd.comm_cxt.g_receiver_loop_poll_up = COMM_STAT_TIME(); - - cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); - ss_rc = memset_s(ckey.name, NAMEDATALEN, 0x0, NAMEDATALEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s(ckey.name, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - ckey.name[cpylen] = '\0'; - - LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_nodename_node_idx_lock); - nodename_entry* entry_name = (nodename_entry*)hash_search(g_htab_nodename_node_idx, &ckey, HASH_FIND, &found); - if (found) { - cmailbox_idx = entry_name->entry.val; - } - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock); - - if (cmailbox_idx < 0) { - errno = ECOMMTCPREMOETECLOSE; - return -1; - } - cmailbox = &C_MAILBOX(cmailbox_idx, streamid); - - struct iovec* iov = NULL; - struct mc_lqueue_item* iov_item = NULL; - // use share memory malloc for buffer received data message - if (libcomm_malloc_iov_item(&iov_item, IOV_DATA_SIZE) != 0) { - return -1; - } - iov = iov_item->element.data; - - // copy the datat to the buffer of executor - ss_rc = memcpy_s(iov->iov_base, IOV_DATA_SIZE, message, m_len); - securec_check(ss_rc, "\0", "\0"); - iov->iov_len = m_len; - - if (gs_push_cmailbox_buffer(cmailbox, iov_item, cmailbox_version) < 0) { - libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE); - return -1; - } - - /* - * This process is invoked when a DN sends a message to itself. - * When the libcomm sends data to the local node, the data is directly inserted into the memory - * and needs to be proactively notified to the listener. - */ - if (ENABLE_THREAD_POOL_DN_LOGICCONN) { - NotifyListener(cmailbox, false, __FUNCTION__); - } - return m_len; -} - -/* - * function name : gs_r_send_start_ctrl_msg - * description : consumer send local thread id to producer, - * it is means consumer start to receive data, - * so we also send quota to producer. - * notice : we must get mailbox lock before. - * arguments : - * _in_ cmailbox: logic conntion info. - * return value : - * false: failed. - * true : succeed. - */ -static bool gs_r_form_start_ctrl_msg(c_mailbox* cmailbox, FCMSG_T* msg) -{ - pid_t local_tid = t_thrd.comm_cxt.MyPid; - errno_t ss_rc; - uint32 cpylen; - - if (cmailbox->query_id != DEBUG_QUERY_ID) { - cmailbox->query_id = DEBUG_QUERY_ID; - } - - // send local thread id and quota to remote - if (local_tid != cmailbox->local_thread_id) { - int node_idx = cmailbox->idx; - int streamid = cmailbox->streamid; - long add_quota = 0; - - if (g_instance.comm_cxt.quota_cxt.g_having_quota) { - add_quota = gs_add_quota_size(cmailbox); - } - - // change local stream state and quota first - cmailbox->local_thread_id = local_tid; - cmailbox->bufCAP += add_quota; - cmailbox->state = MAIL_RUN; - - // then change remote stream state and quota - msg->type = CTRL_PEER_TID; - msg->node_idx = node_idx; - msg->streamid = streamid; - msg->streamcap = add_quota; - msg->version = cmailbox->remote_version; - msg->extra_info = cmailbox->local_thread_id; - msg->query_id = cmailbox->query_id; - - cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); - ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - msg->nodename[cpylen] = '\0'; - - return true; - } - - return false; -} // gs_r_send_start_ctrl_msg - -static void update_cmailbox_statistic(struct c_mailbox* cmailbox, check_cmailbox_option opt, int n_got_data) -{ - libcomm_time_record* time_record = opt.time_record; - if (cmailbox->statistic != NULL) { - time_record->wait_data_time = ABS_SUB(time_record->wait_data_end, time_record->wait_data_start); - cmailbox->statistic->wait_data_time += time_record->wait_data_time; - - time_record->time_now = COMM_STAT_TIME(); - cmailbox->statistic->wait_lock_time += ABS_SUB(time_record->time_now, time_record->wait_lock_start); - - if (cmailbox->statistic->first_poll_time == 0) { - cmailbox->statistic->first_poll_time = time_record->time_enter; - cmailbox->statistic->consumer_elapsed_time += - ABS_SUB(time_record->time_enter, cmailbox->statistic->start_time); - } else { - cmailbox->statistic->consumer_elapsed_time += - ABS_SUB(time_record->time_enter, t_thrd.comm_cxt.g_consumer_process_duration); - } - - if (opt.first_cycle) { - cmailbox->statistic->call_poll_count++; - cmailbox->statistic->last_poll_time = time_record->time_enter; - } - - if (n_got_data > 0 || opt.poll_error_flag == 1) { - cmailbox->statistic->total_poll_time += ABS_SUB(time_record->time_now, time_record->time_enter); - } - } -} - -static void gs_update_producer(struct c_mailbox* cmailbox, int* producer, int* n_got_data, int poll_error_flag) -{ - // there is data in the mailbox already - if (cmailbox->buff_q->count > 0) { - (*n_got_data)++; - // set the having label for Consumer thread - *producer = WAIT_POLL_FLAG_GOT; - } - // need return - if (*n_got_data > 0 || poll_error_flag == 1) { - /* gs_wait_poll will return, clean semaphore */ - cmailbox->semaphore = NULL; - if (*producer == WAIT_POLL_FLAG_WAIT) { - *producer = WAIT_POLL_FLAG_IDLE; - } - } else { - /* gs_wait_poll will enter gs_poll, regist semaphore */ - cmailbox->semaphore = t_thrd.comm_cxt.libcomm_semaphore; - *producer = WAIT_POLL_FLAG_WAIT; - } -} - -// check if there is data in the c_mailbox of the given node index and stream index already -int gs_check_cmailbox_data(const gsocket* gs_sock_array, // array of producers node index - int nproducer, // number of producers - int* producer, // producers number triggers poll - bool close_expected, // is logic connection closed by remote is an expected result - check_cmailbox_option opt) -{ - int n_got_data = 0; - int idx = -1; - int streamid = -1; - int version = -1; - libcomm_time_record* time_record = opt.time_record; - COMM_TIMER_COPY_INIT(time_record->t_begin); - struct c_mailbox* cmailbox = NULL; - struct FCMSG_T fcmsgs = {0x0}; - for (int i = 0; i < nproducer; i++) { - idx = gs_sock_array[i].idx; - streamid = gs_sock_array[i].sid; - version = gs_sock_array[i].ver; - Assert(idx >= 0 && idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num && streamid > 0 && - streamid < g_instance.comm_cxt.counters_cxt.g_max_stream_num); - - // get the cmailbox then lock it - cmailbox = &C_MAILBOX(idx, streamid); - LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); - - // check the state of the mailbox is correct - // ret -2 means close by remote - if (false == gs_check_mailbox(cmailbox->local_version, version)) { - if (!close_expected) { - MAILBOX_ELOG(cmailbox, WARNING, "(r|wait poll)\tStream has already closed, detail:%s.", - mc_strerror(cmailbox->close_reason)); - } - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - errno = cmailbox->close_reason; - // set the error flag for Consumer thread - producer[i] = WAIT_POLL_FLAG_ERROR; - return -2; - } - - gs_update_producer(cmailbox, &producer[i], &n_got_data, opt.poll_error_flag); - - /* update the statistic information of the mailbox */ - update_cmailbox_statistic(cmailbox, opt, n_got_data); - - // send local thread id and quota to remote - bool send_msg = gs_r_form_start_ctrl_msg(cmailbox, &fcmsgs); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - - // send local thread id and quota to remote without cmailbox lock - if (send_msg && (gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[idx], &fcmsgs, ROLE_CONSUMER) <= 0)) { - errno = ECOMMTCPTCPDISCONNECT; - return -1; - } - } - // if data is found, return the number of mailboxes which have data - // or, if it is waked up here but no data found, there must be interruption, we should return and - // CHECK_FOR_INTERRUPT - // - if (n_got_data > 0) { - COMM_TIMER_LOG("(r|wait poll)\tGet data for node[%d,%d]:%s.", idx, streamid, - REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx)); - return n_got_data; - } else if (opt.poll_error_flag == 1) { - COMM_DEBUG_LOG("(r|wait poll)\tWaked up but no data."); - errno = ECOMMTCPWAITPOLLERROR; - return -1; - } - return 0; -} - -void gs_check_all_producers_mailbox(const gsocket* gs_sock_array, int nproducer, int* producer) -{ - struct c_mailbox* cmailbox = NULL; - for (int i = 0; i < nproducer; i++) { - if (producer[i] == WAIT_POLL_FLAG_WAIT) { - producer[i] = WAIT_POLL_FLAG_IDLE; - cmailbox = &C_MAILBOX(gs_sock_array[i].idx, gs_sock_array[i].sid); - LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); - if (gs_check_mailbox(cmailbox->local_version, gs_sock_array[i].ver) == true) { - cmailbox->semaphore = NULL; - } - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - } - } -} - -/* Handle the same message and print message receiving and sending log. */ -void gs_comm_ipc_print(MessageIpcLog *ipc_log, char *remotenode, gsocket *gs_sock, CommMsgOper msg_oper) -{ - /* if the same number of messages is greater than 1, print this message. */ - if (ipc_log->last_msg_count > 1) { - ereport(LOG, - (errmsg("(%s) comm_ipc_log, msgtype:%c, total_len:%d, msg_count:%d, node:%s, last msg time:%s.", - msg_oper_string(msg_oper), ipc_log->last_msg_type, ipc_log->last_msg_len, - ipc_log->last_msg_count, remotenode, ipc_log->last_msg_time))); - } - - /* print current message */ - if ((gs_sock != NULL && gs_sock->idx == 0 && gs_sock->sid == 0) || gs_sock == NULL) { - ereport(LOG, (errmsg("(%s) comm_ipc_log, msgtype:%c, len:%d, node:%s.", - msg_oper_string(msg_oper), ipc_log->type, ipc_log->msg_len, remotenode))); - } else { - ereport(LOG, (errmsg("(%s) comm_ipc_log, msgtype:%c, len:%d, node:%s[nid:%d,sid:%d].", - msg_oper_string(msg_oper), ipc_log->type, ipc_log->msg_len, - remotenode, gs_sock->idx, gs_sock->sid))); - } -} - -// cancel request for receiver (called by die() or StatementCancelHandler() in postgresMain ) -void gs_r_cancel() -{ - // use g_cancel_requested save DEBUG_QUERY_ID as a flag - g_instance.comm_cxt.reqcheck_cxt.g_cancel_requested = - (t_thrd.proc_cxt.MyProcPid != 0) ? t_thrd.proc_cxt.MyProcPid : 1; -} - -// receiver close logic stream, call by Consumer thread -// when no data need or all data are received, or error happed -int gs_r_close_stream(gsocket* gsock) -{ - int node_idx = gsock->idx; - int stream_idx = gsock->sid; - int version = gsock->ver; - int type = gsock->type; - struct FCMSG_T fcmsgs = {0x0}; - - if ((node_idx < 0) || (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) || (stream_idx <= 0) || - (stream_idx >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) || (type == GSOCK_PRODUCER)) { - COMM_DEBUG_LOG("(r|cls stream)\tInvalid argument: node idx[%d], stream id[%d], type[%d].", - node_idx, - stream_idx, - type); - errno = ECOMMTCPARGSINVAL; - return -1; - } - - AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); - - // step 1: get the mailbox and check the state of the cmailbox - // if it is closed, delete the entry in the hash table (g_r_htab_nodeid_skey_to_stream) - // - struct c_mailbox* cmailbox = &(C_MAILBOX(node_idx, stream_idx)); - if (cmailbox->state == MAIL_CLOSED) { - return 0; - } - - LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); - - // if it was closed or reused, we need do nothing here, - // but we will do close poll and delete the entry for sure, - // there is no side effect - if (gs_check_mailbox(cmailbox->local_version, version) == false) { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - return 0; - } - - // step 2: reset the cmailbox, close poll and delete the entry in hash table (g_r_htab_nodeid_skey_to_stream) - // - COMM_DEBUG_LOG("(r|cls stream)\tTo close stream[%d] for node[%d]:%s.", - stream_idx, - node_idx, - REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx)); - - gs_r_close_logic_connection(cmailbox, ECOMMTCPAPPCLOSE, &fcmsgs); - - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - - // Send close ctrl msg to remote without cmailbox lock - (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[node_idx], &fcmsgs, ROLE_CONSUMER); - - return 0; -} // gs_r_close_stream - -// sender close logic stream, call by Producer thread when it failed to send data -int gs_s_close_stream(gsocket* gsock) -{ - int node_idx = gsock->idx; - int stream_idx = gsock->sid; - int version = gsock->ver; - int type = gsock->type; - struct FCMSG_T fcmsgs = {0x0}; - - if ((node_idx < 0) || (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) || (stream_idx <= 0) || - (stream_idx >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) || (type == GSOCK_CONSUMER)) { - COMM_DEBUG_LOG("(s|cls stream)\tInvalid argument: node idx[%d], stream id[%d], type[%d].", - node_idx, - stream_idx, - type); - errno = ECOMMTCPARGSINVAL; - return -1; - } - - AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); - - // step 1: get the mailbox and check the state of the cmailbox, - // if the keys of the pmailbox is not matched, return error - // - struct p_mailbox* pmailbox = &P_MAILBOX(node_idx, stream_idx); - LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock); - - if (gs_check_mailbox(pmailbox->local_version, version) == false) { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); - return 0; - } - - // step 2: reset the state of the pmailbox - // - COMM_DEBUG_LOG("(s|cls stream)\tTo close stream[%d] for node[%d]:%s.", - stream_idx, - node_idx, - REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx)); - - gs_s_close_logic_connection(pmailbox, ECOMMTCPAPPCLOSE, &fcmsgs); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); - // Send close ctrl msg to remote without pmailbox lock - (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, ROLE_PRODUCER); - - return 0; -} - -// close logic socket, it will return when the sock type is invalid. -void gs_close_gsocket(gsocket* gsock) -{ - bool TempImmediateInterruptOK = t_thrd.int_cxt.ImmediateInterruptOK; - t_thrd.int_cxt.ImmediateInterruptOK = false; - - if (gsock->type == GSOCK_INVALID) { - LIBCOMM_INTERFACE_END(false, TempImmediateInterruptOK); - return; - } - - AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); - - if (gsock->type == GSOCK_DAUL_CHANNEL || gsock->type == GSOCK_PRODUCER) { - (void)gs_s_close_stream(gsock); - } - - if (gsock->type == GSOCK_DAUL_CHANNEL || gsock->type == GSOCK_CONSUMER) { - (void)gs_r_close_stream(gsock); - } - - *gsock = GS_INVALID_GSOCK; - - LIBCOMM_INTERFACE_END(false, TempImmediateInterruptOK); - return; -} - -bool gs_stop_query(gsocket* gsock, uint32 remote_pid) -{ - struct FCMSG_T fcmsgs = {0x0}; - int rc; - errno_t ss_rc; - uint32 cpylen; - - fcmsgs.type = CTRL_STOP_QUERY; - fcmsgs.node_idx = gsock->idx; - fcmsgs.streamid = gsock->sid; - fcmsgs.version = gsock->ver; - fcmsgs.query_id = DEBUG_QUERY_ID; - fcmsgs.extra_info = remote_pid; - cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); - ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strncpy_s(fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); - securec_check(ss_rc, "\0", "\0"); - fcmsgs.nodename[cpylen] = '\0'; - - rc = gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[gsock->idx], &fcmsgs, ROLE_CONSUMER); - - return (rc > 0); -} - -/* get the error information of communication layer */ -const char* gs_comm_strerror() -{ - bool savedVal = t_thrd.int_cxt.ImmediateInterruptOK; - t_thrd.int_cxt.ImmediateInterruptOK = false; - const char *errMsg = mc_strerror(errno); - t_thrd.int_cxt.ImmediateInterruptOK = savedVal; - return errMsg; -} - -/* get communication layer stream status at receiver end as a tuple for pg_comm_stream_status */ -bool get_next_recv_stream_status(CommRecvStreamStatus* stream_status) -{ - int idx, sid; - uint32 time_now = (uint32)mc_timers_ms(); - uint64 run_time = 0; - struct c_mailbox* cmailbox = NULL; - - /* if node index is invalid or stream index is invalid, return false */ - if (stream_status->idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num || - stream_status->stream_id >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) { - return false; - } - - /* traves all mailbox and get the stream status in C_MAILBOX. */ - for (idx = stream_status->idx; idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; idx++) { - for (sid = stream_status->stream_id + 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { - /* do not need return the closed stream status. */ - cmailbox = &C_MAILBOX(idx, sid); - if (cmailbox->state != MAIL_CLOSED) { - stream_status->idx = cmailbox->idx; - stream_status->stream_id = cmailbox->streamid; - stream_status->stream_state = stream_stat_string(cmailbox->state); - stream_status->quota_size = cmailbox->bufCAP; - stream_status->query_id = cmailbox->query_id; - stream_status->stream_key = cmailbox->stream_key; - stream_status->buff_usize = cmailbox->buff_q->u_size; - stream_status->bytes = cmailbox->statistic ? cmailbox->statistic->recv_bytes : 0; - stream_status->local_thread_id = cmailbox->local_thread_id; - stream_status->peer_thread_id = cmailbox->peer_thread_id; - stream_status->time = cmailbox->statistic ? (uint64)(time_now - cmailbox->statistic->start_time) : 0; - - run_time = (stream_status->time > 0) ? stream_status->time : 1; - stream_status->speed = stream_status->bytes * 1000 / run_time; - - stream_status->tcp_sock = g_instance.comm_cxt.g_r_node_sock[idx].ctrl_tcp_sock; - errno_t ss_rc; - ss_rc = strcpy_s( - stream_status->remote_host, HOST_ADDRSTRLEN, g_instance.comm_cxt.g_r_node_sock[idx].remote_host); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strcpy_s( - stream_status->remote_node, NAMEDATALEN, g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename); - securec_check(ss_rc, "\0", "\0"); - - return true; - } - } - /* set stream index to -1 for next node */ - stream_status->stream_id = -1; - } - - return false; -} - -/* get communication layer stream status at sender end as a tuple for pg_comm_send_stream */ -bool get_next_send_stream_status(CommSendStreamStatus* stream_status) -{ - int idx, sid; - uint32 time_now = (uint32)mc_timers_ms(); - uint64 run_time = 0; - struct p_mailbox* pmailbox = NULL; - - /* if node index is invalid or stream index is invalid, return false */ - if (stream_status->idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num || - stream_status->stream_id >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) { - return false; - } - - /* traves all mailbox and get the stream status in P_MAILBOX. */ - for (idx = stream_status->idx; idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; idx++) { - for (sid = stream_status->stream_id + 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { - /* no need return the closed stream status */ - pmailbox = &P_MAILBOX(idx, sid); - if (pmailbox->state != MAIL_CLOSED) { - stream_status->idx = pmailbox->idx; - stream_status->stream_id = pmailbox->streamid; - stream_status->stream_state = stream_stat_string(pmailbox->state); - stream_status->quota_size = pmailbox->bufCAP; - stream_status->query_id = pmailbox->query_id; - stream_status->stream_key = pmailbox->stream_key; - stream_status->bytes = pmailbox->statistic ? pmailbox->statistic->send_bytes : 0; - stream_status->wait_quota = pmailbox->statistic ? (uint64)pmailbox->statistic->wait_quota_overhead : 0; - stream_status->local_thread_id = pmailbox->local_thread_id; - stream_status->peer_thread_id = pmailbox->peer_thread_id; - stream_status->time = pmailbox->statistic ? (uint64)(time_now - pmailbox->statistic->start_time) : 0; - stream_status->tcp_sock = g_instance.comm_cxt.g_s_node_sock[idx].ctrl_tcp_sock; - - run_time = (stream_status->time > 0) ? stream_status->time : 1; - stream_status->speed = stream_status->bytes * 1000 / run_time; - - errno_t ss_rc; - ss_rc = strcpy_s( - stream_status->remote_host, HOST_ADDRSTRLEN, g_instance.comm_cxt.g_s_node_sock[idx].remote_host); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strcpy_s( - stream_status->remote_node, NAMEDATALEN, g_instance.comm_cxt.g_s_node_sock[idx].remote_nodename); - securec_check(ss_rc, "\0", "\0"); - - return true; - } - } - /* set stream index to -1 for next node */ - stream_status->stream_id = -1; - } - - return false; -} - -/* - * function name : get_next_comm_delay_info - * description : get libcomm delay info with delay_info->idx . - * arguments : _in_ delay_info->idx: the node index. - * _out_ delay_info: return delay info - * return value : - * true: return delay info. - * false: no delay info. - */ -bool get_next_comm_delay_info(CommDelayInfo* delay_info) -{ - int node_idx = delay_info->idx; - int array_idx = -1; - uint32 delay = 0; - uint32 delay_min = 0; - uint32 delay_max = 0; - uint32 delay_sum = 0; - - if (g_instance.comm_cxt.g_delay_survey_switch == false) { - g_instance.comm_cxt.g_delay_survey_start_time = mc_timers_ms(); - LIBCOMM_ELOG(LOG, "delay survey switch is open"); - } - g_instance.comm_cxt.g_delay_survey_switch = true; - - /* if node index is invalid, return false */ - if (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) { - return false; - } - - for (;;) { - if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id == 0) { - node_idx++; - } else { - break; - } - - if (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) { - return false; - } - } - - /* calculate delay info */ - for (array_idx = 0; array_idx < MAX_DELAY_ARRAY_INDEX; array_idx++) { - delay = g_instance.comm_cxt.g_delay_info[node_idx].delay[array_idx]; - if (delay < delay_min || delay_min == 0) { - delay_min = delay; - } - if (delay > delay_max) { - delay_max = delay; - } - delay_sum += delay; - } - - /* save delay info in delay_info */ - errno_t ss_rc; - ss_rc = strcpy_s(delay_info->remote_host, HOST_ADDRSTRLEN, g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host); - securec_check(ss_rc, "\0", "\0"); - ss_rc = strcpy_s(delay_info->remote_node, NAMEDATALEN, g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename); - securec_check(ss_rc, "\0", "\0"); - delay_info->stream_num = - g_instance.comm_cxt.counters_cxt.g_max_stream_num - g_instance.comm_cxt.g_usable_streamid[node_idx].count - 1; - delay_info->min_delay = delay_min; - delay_info->dev_delay = delay_sum / MAX_DELAY_ARRAY_INDEX; - delay_info->max_delay = delay_max; - - /* move to next node index */ - delay_info->idx = node_idx + 1; - - return true; -} - -/* get communication layer status as a tuple for pg_comm_status */ -bool gs_get_comm_stat(CommStat* comm_stat) -{ - if (comm_stat == NULL || g_instance.comm_cxt.counters_cxt.g_cur_node_num == 0) { - return false; - } - - if (g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate != NULL) { - comm_stat->postmaster = g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate[POSTMASTER]; - } - - if (g_instance.attr.attr_storage.comm_cn_dn_logic_conn == false && IS_PGXC_COORDINATOR) { - return true; - } - - int idx, sid, i; - int used_stream = 0; - struct c_mailbox* cmailbox = NULL; - struct p_mailbox* pmailbox = NULL; - - const int G_CUR_NODE_NUM = g_instance.comm_cxt.counters_cxt.g_cur_node_num; - int *libcomm_used_rate = g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate; - long recv_bytes[G_CUR_NODE_NUM]; - int recv_count[G_CUR_NODE_NUM]; - int recv_count_speed = 0; - long recv_speed = 0; - - long send_speed = 0; - long send_bytes[G_CUR_NODE_NUM]; - int send_count[G_CUR_NODE_NUM]; - int send_count_speed = 0; - - errno_t rc = memset_s(recv_bytes, sizeof(recv_bytes), 0, sizeof(recv_bytes)); - securec_check(rc, "\0", "\0"); - rc = memset_s(recv_count, sizeof(recv_count), 0, sizeof(recv_count)); - securec_check(rc, "\0", "\0"); - rc = memset_s(send_bytes, sizeof(send_bytes), 0, sizeof(send_bytes)); - securec_check(rc, "\0", "\0"); - rc = memset_s(send_count, sizeof(send_count), 0, sizeof(send_count)); - securec_check(rc, "\0", "\0"); - - if (libcomm_used_rate != NULL) { - comm_stat->postmaster = libcomm_used_rate[POSTMASTER]; - comm_stat->gs_sender_flow = libcomm_used_rate[GS_SEND_flow]; - comm_stat->gs_receiver_flow = libcomm_used_rate[GS_RECV_FLOW]; - comm_stat->gs_receiver_loop = libcomm_used_rate[GS_RECV_LOOP]; - for (i = GS_RECV_LOOP + 1; i < g_instance.comm_cxt.counters_cxt.g_recv_num + GS_RECV_LOOP; i++) { - if (comm_stat->gs_receiver_loop < libcomm_used_rate[i]) { - comm_stat->gs_receiver_loop = libcomm_used_rate[i]; - } - } - } - - /* sum of recv_speed/send_speed in all stream */ - i = 0; - while (i < 2) { - /* idx: node index */ - for (idx = 0; idx < G_CUR_NODE_NUM; idx++) { - if (i == 0) { - recv_bytes[idx] = g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_bytes; - recv_count[idx] = g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_count; - send_bytes[idx] = g_instance.comm_cxt.g_senders->sender_conn[idx].comm_bytes; - send_count[idx] = g_instance.comm_cxt.g_senders->sender_conn[idx].comm_count; - /* sid: stream index */ - for (sid = 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { - cmailbox = &C_MAILBOX(idx, sid); - if (cmailbox->state != MAIL_CLOSED) { - comm_stat->buffer += cmailbox->buff_q->u_size; - } - - pmailbox = &P_MAILBOX(idx, sid); - if (pmailbox->state != MAIL_CLOSED) { - used_stream++; - } - } - } else if (i == 1) { - recv_speed += g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_bytes - recv_bytes[idx]; - recv_count_speed += g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_count - recv_count[idx]; - send_speed += g_instance.comm_cxt.g_senders->sender_conn[idx].comm_bytes - send_bytes[idx]; - send_count_speed += g_instance.comm_cxt.g_senders->sender_conn[idx].comm_count - send_count[idx]; - } - } - i++; - sleep(0.1); - } - - comm_stat->recv_speed = recv_speed * 10 / 1024; - comm_stat->recv_count_speed = recv_count_speed * 10; - comm_stat->send_speed = send_speed * 10 / 1024; - comm_stat->send_count_speed = send_count_speed * 10; - comm_stat->mem_libcomm = libcomm_used_memory; - comm_stat->mem_libpq = libpq_used_memory; - comm_stat->stream_conn_num = used_stream; - return true; -} - -/* Output the contents of structure into log file */ -void gs_log_comm_status() -{ - LIBCOMM_ELOG(LOG, "[LOG STATUS]Comm Layer Status: Do nothing now, please ignore it."); -} - -int gs_close_all_stream_by_debug_id(uint64 query_id) -{ - int idx, sid; - struct c_mailbox* cmailbox = NULL; - struct p_mailbox* pmailbox = NULL; - int cmailbox_count = 0; - int pmailbox_count = 0; - struct FCMSG_T fcmsgs = {0x0}; - - if (query_id == 0) { - LIBCOMM_ELOG(WARNING, "(cls all stream)\tInvalid argument: query id is 0!"); - return -1; - } - - AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); - - for (idx = 0; idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; idx++) { - for (sid = 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { - cmailbox = &C_MAILBOX(idx, sid); - if (cmailbox->query_id == query_id && cmailbox->stream_key.planNodeId != 0) { - LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); - - if (cmailbox->query_id == query_id && cmailbox->stream_key.planNodeId != 0 && - cmailbox->state != MAIL_CLOSED) { - cmailbox_count++; - gs_r_close_logic_connection(cmailbox, ECOMMTCPAPPCLOSE, &fcmsgs); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - /* Send close ctrl msg to remote without pmailbox lock */ - (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[idx], &fcmsgs, ROLE_CONSUMER); - } else { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); - } - } - - pmailbox = &P_MAILBOX(idx, sid); - if (pmailbox->query_id == query_id && pmailbox->stream_key.planNodeId != 0) { - LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock); - - if (pmailbox->query_id == query_id && pmailbox->stream_key.planNodeId != 0 && - pmailbox->state != MAIL_CLOSED) { - pmailbox_count++; - gs_s_close_logic_connection(pmailbox, ECOMMTCPAPPCLOSE, &fcmsgs); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); - /* Send close ctrl msg to remote without pmailbox lock */ - (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[idx], &fcmsgs, ROLE_PRODUCER); - } else { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); - } - } - } - } - - if (cmailbox_count != 0 || pmailbox_count != 0) { - LIBCOMM_ELOG(LOG, - "(cls all stream)\tClose all stream by debug id[%lu], close %d cmailbox and %d pmailbox.", - query_id, - cmailbox_count, - pmailbox_count); - } - return 0; -} - -void SetupCommSignalHook() -{ - (void)gspqsignal(SIGINT, SIG_IGN); - (void)gspqsignal(SIGUSR1, SIG_IGN); - (void)gspqsignal(SIGPIPE, SIG_IGN); - - (void)gspqsignal(SIGTERM, SIG_IGN); - (void)gspqsignal(SIGQUIT, SIG_IGN); - (void)gspqsignal(SIGALRM, SIG_IGN); - (void)gspqsignal(SIGUSR2, SIG_IGN); - (void)gspqsignal(SIGFPE, SIG_IGN); - (void)gspqsignal(SIGCHLD, SIG_IGN); - /* when support guc online change, we can accept sighup, but now we don't handle it */ - (void)gspqsignal(SIGHUP, SIG_IGN); -} - -/* SIGTERM: set flag to exit normally */ -static void PoolCleanerShutdownHandler(SIGNAL_ARGS) -{ - int save_errno = errno; - - t_thrd.poolcleaner_cxt.shutdown_requested = true; - - if (t_thrd.proc) - SetLatch(&t_thrd.proc->procLatch); - - errno = save_errno; -} - -/* SIGHUP: re-read config file */ -static void PoolCleanerSighupHandler(SIGNAL_ARGS) -{ - int save_errno = errno; - - t_thrd.poolcleaner_cxt.got_SIGHUP = true; - if (t_thrd.proc) - SetLatch(&t_thrd.proc->procLatch); - - errno = save_errno; -} - -void SetupPoolerCleanSignalHook() -{ - (void)gspqsignal(SIGHUP, PoolCleanerSighupHandler); - (void)gspqsignal(SIGQUIT, SIG_IGN); - (void)gspqsignal(SIGTERM, PoolCleanerShutdownHandler); - (void)gspqsignal(SIGINT, PoolCleanerShutdownHandler); /* cancel current query */ - (void)gspqsignal(SIGALRM, SIG_IGN); /* timeout conditions */ - (void)gspqsignal(SIGPIPE, SIG_IGN); - (void)gspqsignal(SIGUSR1, SIG_IGN); - (void)gspqsignal(SIGUSR2, SIG_IGN); - (void)gspqsignal(SIGFPE, FloatExceptionHandler); - (void)gspqsignal(SIGCHLD, SIG_DFL); -} - -#ifdef ENABLE_MULTIPLE_NODES -void init_clean_pooler_idle_connections() -{ - /* we are a postmaster subprocess now */ - IsUnderPostmaster = true; - t_thrd.role = COMM_POOLER_CLEAN; - - /* reset t_thrd.proc_cxt.MyProcPid */ - t_thrd.proc_cxt.MyProcPid = gs_thread_self(); - - t_thrd.proc_cxt.MyProgName = "commPoolerCleaner"; - - /* record Start Time for logging */ - t_thrd.proc_cxt.MyStartTime = time(NULL); - - /* Identify myself via ps */ - init_ps_display("pooler cleaner process", "", "", ""); - - /* set processing mode */ - SetProcessingMode(InitProcessing); - - /* setup signal process hook */ - SetupPoolerCleanSignalHook(); - - /* We allow SIGQUIT (quickdie) at all times */ - sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); - - /* Early initialization */ - BaseInit(); - -#ifndef EXEC_BACKEND - InitProcess(); -#endif - - u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp(); - return; -} - -extern void clean_pooler_idle_connections(void); -void commPoolCleanerMain() -{ - sigjmp_buf local_sigjmp_buf; - uint64 current_time, last_start_time, poolerMaxIdleTime, nodeNameHashVal; - MemoryContext poolCleaner_context; - char *curNodeName = g_instance.attr.attr_common.PGXCNodeName; - const Size nodeNameHashMaxVal = 60000; - - ereport(LOG, (errmsg("commPoolCleanerMain started"))); - init_clean_pooler_idle_connections(); - - /* - * Create the memory context we will use in the main loop. - * - * t_thrd.mem_cxt.msg_mem_cxt is reset once per iteration of the main loop, ie, upon - * completion of processing of each command message from the client. - */ - poolCleaner_context = AllocSetContextCreate(t_thrd.top_mem_cxt, "Pool Cleaner", ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - - (void)MemoryContextSwitchTo(poolCleaner_context); - - /* If an exception is encountered, processing resumes here. */ - int curTryCounter; - int* oldTryCounter = NULL; - - /* Normal exit */ - if (t_thrd.poolcleaner_cxt.shutdown_requested) { - g_instance.pid_cxt.CommPoolerCleanPID = 0; - proc_exit(0); /* done */ - } - - if (sigsetjmp(local_sigjmp_buf, 1) != 0) { - gstrace_tryblock_exit(true, oldTryCounter); - - /* Prevents interrupts while cleaning up */ - HOLD_INTERRUPTS(); - - /* Report the error to the server log */ - EmitErrorReport(); - - /* release resource held by lsc */ - AtEOXact_SysDBCache(false); - - (void)MemoryContextSwitchTo(poolCleaner_context); - FlushErrorState(); - - /* Flush any leaked data in the top-level context */ - MemoryContextResetAndDeleteChildren(poolCleaner_context); - - /* - * process exit. Note that because we called InitProcess, a - * callback was registered to do ProcKill, which will clean up - * necessary state. - */ - proc_exit(0); - } - oldTryCounter = gstrace_tryblock_entry(&curTryCounter); - - /* We can now handle ereport(ERROR) */ - t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; - - /* - * Unblock signals (they were blocked when the postmaster forked us) - */ - gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); - (void)gs_signal_unblock_sigusr2(); - - /* report this backend in the PgBackendStatus array */ - pgstat_report_appname("PoolCleaner"); - - /* - * Create a resource owner to keep track of our resources (currently only - * buffer pins). - */ - t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Pool cleaner", - THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_COMMUNICATION)); - SetProcessingMode(NormalProcessing); - - /* To ensure the minimum pool concept, do not clean the idle connections of each CN at the same time. - * Time difference: within 60s - */ - if (curNodeName != NULL) { - nodeNameHashVal = (uint64)(string_hash((void*)curNodeName, (strlen(curNodeName) + 1))) % nodeNameHashMaxVal; - } - - last_start_time = mc_timers_ms() + nodeNameHashVal; - for (;;) { - if (t_thrd.poolcleaner_cxt.shutdown_requested == true || g_instance.status > NoShutdown) { - break; - } - - if (t_thrd.poolcleaner_cxt.got_SIGHUP) { - t_thrd.poolcleaner_cxt.got_SIGHUP = false; - ProcessConfigFile(PGC_SIGHUP); - } - - sleep(1); - current_time = mc_timers_ms(); - poolerMaxIdleTime = (u_sess->attr.attr_network.PoolerMaxIdleTime) * MS_PER_S; - - /* If pooler_maximum_idle_time is zero, do not call clean connection procedure */ - if (poolerMaxIdleTime == 0) { - continue; - } - - if (IS_PGXC_COORDINATOR && (current_time - last_start_time) >= poolerMaxIdleTime) { - clean_pooler_idle_connections(); - last_start_time = current_time; - } - } - - /* All done, go away */ - g_instance.pid_cxt.CommPoolerCleanPID = 0; - proc_exit(0); -} -#endif - -- 2.34.1 From a45c2878b261822c4c5e7df0a51c28048d43841a Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 21:05:44 +0800 Subject: [PATCH 35/56] ADD file via upload --- src/gausskernel/cbb/communication/libcomm.cpp | 2870 +++++++++++++++++ 1 file changed, 2870 insertions(+) create mode 100644 src/gausskernel/cbb/communication/libcomm.cpp diff --git a/src/gausskernel/cbb/communication/libcomm.cpp b/src/gausskernel/cbb/communication/libcomm.cpp new file mode 100644 index 000000000..e1aff3ea5 --- /dev/null +++ b/src/gausskernel/cbb/communication/libcomm.cpp @@ -0,0 +1,2870 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * libcomm.cpp + * + * IDENTIFICATION + * src/gausskernel/cbb/communication/libcomm.cpp + * + * ------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libcomm_core/mc_tcp.h" +#include "libcomm_core/mc_poller.h" +#include "libcomm_utils/libcomm_thread.h" +#include "libcomm_utils/libcomm_lqueue.h" +#include "libcomm_utils/libcomm_queue.h" +#include "libcomm_utils/libcomm_lock_free_queue.h" +#include "distributelayer/streamCore.h" +#include "distributelayer/streamProducer.h" +#include "pgxc/poolmgr.h" +#include "libpq/auth.h" +#include "libpq/pqsignal.h" +#include "storage/ipc.h" +#include "utils/ps_status.h" +#include "utils/dynahash.h" + +#include "vecexecutor/vectorbatch.h" +#include "vecexecutor/vecnodes.h" +#include "executor/exec/execStream.h" +#include "miscadmin.h" +#include "gssignal/gs_signal.h" +#include "pgxc/pgxc.h" +#include "libcomm_common.h" + +#ifdef ENABLE_UT +#define static +#endif + +#define CHECKCONNSTATTIMEOUT 5 + +#ifndef MS_PER_S +#define MS_PER_S 1000 +#endif + + +/* hash tables */ +/* hash table to keep: ip + port -> connection status */ +static HTAB* g_htab_ip_state = NULL; +pthread_mutex_t g_htab_ip_state_lock; + +/* hash table to keep: nodename -> node id */ +static HTAB* g_htab_nodename_node_idx = NULL; +pthread_mutex_t g_htab_nodename_node_idx_lock; +static int nodename_count = 0; + +/* hash table to keep: fd_id -> node id */ +HTAB* g_htab_fd_id_node_idx = NULL; +pthread_mutex_t g_htab_fd_id_node_idx_lock; + +/* for wake up thread */ +static HTAB* g_htab_tid_poll = NULL; +pthread_mutex_t g_htab_tid_poll_lock; + +/* at receiver and sender: hash table to keep: socket -> socket version number, for socket management */ +static HTAB* g_htab_socket_version = NULL; +pthread_mutex_t g_htab_socket_version_lock; + +static ArrayLockFreeQueue g_memory_pool_queue; + +unsigned long IOV_DATA_SIZE = 1024 * 8; +unsigned long IOV_ITEM_SIZE = IOV_DATA_SIZE + sizeof(struct iovec) + sizeof(mc_lqueue_element); +unsigned long DEFULTMSGLEN = 1024 * 8; +unsigned long LIBCOMM_BUFFER_SIZE = 1024 * 8; + +gsocket gs_invalid_gsock = {0, 0, 0, 0}; + +static void gs_s_build_reply_conntion(libcommaddrinfo* addr_info, int remote_version); + +extern GlobalNodeDefinition* global_node_definition; + +extern knl_instance_context g_instance; + +/* + * function name : gs_change_capacity + * description : If GUC parameter "comm_max_datanode" changed this function will be called. + * notice : Only for postmaster thread. + * arguments : + * __in newval: new value (sum of CN and DN). + */ +void gs_change_capacity(int new_node_num) +{ + /* Only postmaster thread can change the g_expect_node_num, + * "g_cur_node_num==0" means postmaster doesn't finish initialization. + */ + if ((t_thrd.proc_cxt.MyProcPid != PostmasterPid) || + (new_node_num == g_instance.comm_cxt.counters_cxt.g_cur_node_num) || + (g_instance.comm_cxt.counters_cxt.g_cur_node_num == 0)) { + return; + } + + /* range for node_num (2,4096) */ + if ((new_node_num > MAX_CN_DN_NODE_NUM) || (new_node_num < MIN_CN_DN_NODE_NUM)) { + LIBCOMM_ELOG(WARNING, "(pm|change capacity)\tInvalidate node num: %d.", new_node_num); + return; + } + + g_instance.comm_cxt.counters_cxt.g_expect_node_num = new_node_num; + g_instance.comm_cxt.quota_cxt.g_quota_changing->post(); + LIBCOMM_ELOG(LOG, + "(pm|change capacity)\tg_cur_node_num [%d], g_expect_node_num [%d].", + g_instance.comm_cxt.counters_cxt.g_cur_node_num, + g_instance.comm_cxt.counters_cxt.g_expect_node_num); +} + +void comm_fill_hash_ctl(HASHCTL* ctl, Size k_size, Size e_size) +{ + ctl->keysize = k_size; + ctl->entrysize = e_size; + ctl->hash = tag_hash; + ctl->hcxt = g_instance.comm_cxt.comm_global_mem_cxt; + return; +} + +void gs_init_hash_table() +{ + AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); + HASHCTL tid_ctl, sock_ver_ctl, sock_id_ctl, nodename_ctl, ipstat_ctl; + int flags, rc; + + /* init g_htab_tid_poll */ + rc = memset_s(&tid_ctl, sizeof(tid_ctl), 0, sizeof(HASHCTL)); + securec_check(rc, "\0", "\0"); + + comm_fill_hash_ctl(&tid_ctl, sizeof(int), sizeof(tid_entry)); + flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX; + g_htab_tid_poll = hash_create("libcomm tid lookup hash", 65535, &tid_ctl, flags); + LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_tid_poll_lock, 0); + + /* init g_htab_socket_version */ + rc = memset_s(&sock_ver_ctl, sizeof(sock_ver_ctl), 0, sizeof(HASHCTL)); + securec_check(rc, "\0", "\0"); + + comm_fill_hash_ctl(&sock_ver_ctl, sizeof(int), sizeof(sock_ver_entry)); + flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX; + g_htab_socket_version = hash_create("libcomm socket version lookup hash", 65535, &sock_ver_ctl, flags); + LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_socket_version_lock, 0); + + /* init g_htab_socket_version */ + rc = memset_s(&sock_id_ctl, sizeof(sock_id_ctl), 0, sizeof(HASHCTL)); + securec_check(rc, "\0", "\0"); + + comm_fill_hash_ctl(&sock_id_ctl, sizeof(sock_id), sizeof(sock_id_entry)); + flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX; + g_htab_fd_id_node_idx = hash_create("libcomm socket & node_idx lookup hash", 65535, &sock_id_ctl, flags); + LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_fd_id_node_idx_lock, 0); + + /* init g_htab_nodename_node_idx */ + rc = memset_s(&nodename_ctl, sizeof(nodename_ctl), 0, sizeof(HASHCTL)); + securec_check(rc, "\0", "\0"); + + comm_fill_hash_ctl(&nodename_ctl, sizeof(char_key), sizeof(nodename_entry)); + flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX; + g_htab_nodename_node_idx = hash_create("libcomm nodename & node_idx lookup hash", 65535, &nodename_ctl, flags); + LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_nodename_node_idx_lock, 0); + + /* init g_htab_socket_version */ + rc = memset_s(&ipstat_ctl, sizeof(ipstat_ctl), 0, sizeof(HASHCTL)); + securec_check(rc, "\0", "\0"); + + comm_fill_hash_ctl(&ipstat_ctl, sizeof(ip_key), sizeof(ip_state_entry)); + flags = HASH_FUNCTION | HASH_ELEM | HASH_SHRCTX; + g_htab_ip_state = hash_create("libcomm ip & status lookup hash", 65535, &ipstat_ctl, flags); + LIBCOMM_PTHREAD_MUTEX_INIT(&g_htab_ip_state_lock, 0); +} + +void gs_set_hs_shm_data(HaShmemData* ha_shm_data) +{ + (void)atomic_set(&g_instance.comm_cxt.g_ha_shm_data, ha_shm_data); +} + +int gs_get_stream_num(void) +{ + return g_instance.comm_cxt.counters_cxt.g_max_stream_num; +} // gs_get_stream_num + +/* + * function name : gs_map_sock_id_to_node_idx + * description : save the key of fd_id and value of node idx into g_htab_fd_id_node_idx + * arguments : + * fd_id: struct of socket and socket id. + * idx: node idx. + * return value : + * 0: succeed. + * -1: save to htab failed. + */ +int gs_map_sock_id_to_node_idx(const sock_id fd_id, int idx) +{ +#ifdef LIBCOMM_FAULT_INJECTION_ENABLE + if (is_comm_fault_injection(LIBCOMM_FI_SOCKID_NODEIDX_FAILED)) { + errno = ECOMMTCPMEMALLOC; + LIBCOMM_ELOG(WARNING, + "(sock_id_to_node_idx)\t[FAULT INJECTION]Failed to save socket[%d,%d] for node[%d].", + fd_id.fd, + fd_id.id, + idx); + return -1; + } +#endif + + bool found = false; + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); + + struct sock_id_entry* entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &fd_id, HASH_ENTER, &found); + entry_id->entry.val = idx; + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); + + return 0; +} + +/* + * function name : gs_set_reply_sock + * description : set reply socket for g_r_node_sock by compare remote node name + * arguments : node_idx, node we want to set reply sock + */ +void gs_set_reply_sock(int node_idx) +{ + for (int recv_idx = 0; recv_idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; recv_idx++) { + if (strcmp(g_instance.comm_cxt.g_r_node_sock[recv_idx].remote_nodename, + g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename) == 0) { + // save reply socket in g_r_node_sock + g_instance.comm_cxt.g_r_node_sock[recv_idx].lock(); + g_instance.comm_cxt.g_r_node_sock[recv_idx].libcomm_reply_sock = + g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket; + g_instance.comm_cxt.g_r_node_sock[recv_idx].libcomm_reply_sock_id = + g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id; + g_instance.comm_cxt.g_r_node_sock[recv_idx].unlock(); + break; + } + } + + return; +} + +/* + * function name : gs_senders_struct_set + * description : initialize socket of local senders, for sending + * notice : retry 10 times inside if failure happens, + * arguments __in node_begin + * __in node_end + */ +void gs_senders_struct_set() +{ + int error = 0; + int i; + + // initialize sender socket and address storage + for (i = 0; i < MAX_CN_DN_NODE_NUM; i++) { + // g_s_node_sock + g_instance.comm_cxt.g_s_node_sock[i].init(); + + // initialize address storage + // initialize socket in gs_connect + error = mc_tcp_addr_init(g_instance.comm_cxt.localinfo_cxt.g_local_host, + 0, + &(g_instance.comm_cxt.g_senders->sender_conn[i].ss), + &(g_instance.comm_cxt.g_senders->sender_conn[i].ss_len)); + if (error != 0) { + ereport(FATAL, + (errmsg("(s|sender init)\tFailed to init sender[%d] for %s.", + i, + g_instance.comm_cxt.localinfo_cxt.g_local_host))); + } + + // set g_instance.comm_cxt.g_senders->sender_conn AND g_sender_count + LIBCOMM_PTHREAD_RWLOCK_INIT(&g_instance.comm_cxt.g_senders->sender_conn[i].rwlock, NULL); + g_instance.comm_cxt.g_senders->sender_conn[i].socket = -1; + g_instance.comm_cxt.g_senders->sender_conn[i].socket_id = -1; + g_instance.comm_cxt.g_senders->sender_conn[i].comm_bytes = 0; + g_instance.comm_cxt.g_senders->sender_conn[i].comm_count = 0; + } + + return; +} + +/* + * function name : gs_update_connection_state + * description : update the connections state of htab + * signal all threads block on the connections + * arguments : addr: structure of IP & PORT + * result: succeed or failed + */ +void gs_update_connection_state(ip_key addr, int result, bool is_signal, int node_idx) +{ + bool found = false; + + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_ip_state_lock); + ip_state_entry* entry_poll = (ip_state_entry*)hash_search(g_htab_ip_state, &addr, HASH_FIND, &found); + if (!found) { + LIBCOMM_ELOG(WARNING, "(s|connect)\tFail to get connection state:port[%s:%d].", addr.ip, addr.port); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); + Assert(found); + return; + } + + COMM_DEBUG_LOG( + "(s|gs_update_connection_state)\tchange connection [%s:%d] state to %d.", addr.ip, addr.port, result); + + /* cannot update connection state when the node idx mismatch */ + if (entry_poll->entry.val.node_idx != node_idx) { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); + return; + } + + entry_poll->entry.val.conn_state = result; + + if (is_signal) { + entry_poll->entry._signal_all(); + } + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); + return; +} + +/* + * function name : gs_s_get_connection_state + * description : gs_s_get_connection_state gives the + * connection state of indicated IP & PORT + * arguments : addr: structure of IP & PORT + * return value : + * CONNSTATECONNECTING: need to build a new connection + * CONNSTATEFAIL: get connection state failed + * CONNSTATESUCCEED: exist a valid connection + */ +int gs_s_get_connection_state(ip_key addr, int node_idx, int type) +{ + int rc = -1; + int old_slot_id = -1; + int retry_count = 0; + int state = CONNSTATEFAIL; + bool found = false; + + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_ip_state_lock); + ip_state_entry* entry_poll = (ip_state_entry*)hash_search(g_htab_ip_state, &addr, HASH_ENTER, &found); + + /* no connection exist before, add in htab and return need to create */ + if (unlikely(!found)) { + COMM_DEBUG_LOG("(s|gs_s_get_connection_state)\tno connection exist [%s:%d].", addr.ip, addr.port); + + entry_poll->entry.val.conn_state = CONNSTATECONNECTING; + entry_poll->entry.val.node_idx = node_idx; + entry_poll->entry._init(); + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); + return CONNSTATECONNECTING; + } + + switch (entry_poll->entry.val.conn_state) { + case CONNSTATECONNECTING: + /* someone is trying to make connetion, wait the result */ + COMM_DEBUG_LOG("(s|gs_s_get_connection_state)\twait for connection start [%s:%d].", addr.ip, addr.port); + + while (entry_poll->entry.val.conn_state == CONNSTATECONNECTING) { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); + rc = entry_poll->entry._timewait(CHECKCONNSTATTIMEOUT); + retry_count++; + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_ip_state_lock); + if (retry_count > (g_instance.comm_cxt.counters_cxt.g_comm_send_timeout / CHECKCONNSTATTIMEOUT)) { + errno = ECOMMTCPCONNTIMEOUT; + break; + } + } + COMM_DEBUG_LOG("(s|gs_s_get_connection_state)\twait for connection end [%s:%d]:%d.", + addr.ip, + addr.port, + entry_poll->entry.val.conn_state); + + /* connect state update became succeed, return CONNSTATESUCCEED */ + if (entry_poll->entry.val.conn_state == CONNSTATESUCCEED) { + state = CONNSTATESUCCEED; + } else { /* connect state is not succeed, return CONNSTATEFAIL */ + errno = (rc == ETIMEDOUT) ? ECOMMTCPCONNTIMEOUT : ECOMMTCPCONNFAIL; + state = CONNSTATEFAIL; + } + break; + + case CONNSTATEFAIL: + COMM_DEBUG_LOG( + "(s|gs_s_get_connection_state)\tconnection invalid, need to create[%s:%d].", addr.ip, addr.port); + + /* connection is failed before, update state to connecting and return need to create */ + entry_poll->entry.val.conn_state = CONNSTATECONNECTING; + entry_poll->entry.val.node_idx = node_idx; + state = CONNSTATECONNECTING; + break; + + case CONNSTATESUCCEED: + /* when the node idx mismatch with the valid connection before + * we assume it as a new connection, update node idx and close + * the old connection + */ + if (node_idx != entry_poll->entry.val.node_idx) { + old_slot_id = entry_poll->entry.val.node_idx; + entry_poll->entry.val.conn_state = CONNSTATECONNECTING; + entry_poll->entry.val.node_idx = node_idx; + state = CONNSTATECONNECTING; + } else { + /* a valid connection in htab, return connection succeed */ + state = CONNSTATESUCCEED; + } + break; + + default: + /* unexpected cases */ + LIBCOMM_ELOG(WARNING, + "(s|connect)\tUnexpected state in checking connection state:port[%s:%d], state:%d, node_idx:%d.", + addr.ip, + addr.port, + entry_poll->entry.val.conn_state, + entry_poll->entry.val.node_idx); + state = CONNSTATEFAIL; + break; + } + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_ip_state_lock); + + /* close old data connection */ + if (old_slot_id != -1) { + LIBCOMM_ELOG(WARNING, + "(s|connect)\tClose the old connections for node%d[%s]:port[%s:%d], type:%d.", + old_slot_id, + REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, old_slot_id), + addr.ip, + addr.port, + type); + if (type == DATA_CHANNEL) { + g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].ip_changed = true; + LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].rwlock); + struct sock_id libcomm_fd_id = {g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].socket, + g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].socket_id}; + gs_s_close_bad_data_socket(&libcomm_fd_id, ECOMMTCPPEERCHANGED, node_idx); + LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_senders->sender_conn[old_slot_id].rwlock); + } else { + g_instance.comm_cxt.g_s_node_sock[node_idx].ip_changed = true; + g_instance.comm_cxt.g_s_node_sock[node_idx].lock(); + struct sock_id ctrl_fd_id = {g_instance.comm_cxt.g_s_node_sock[old_slot_id].ctrl_tcp_sock, + g_instance.comm_cxt.g_s_node_sock[old_slot_id].ctrl_tcp_sock_id}; + gs_s_close_bad_ctrl_tcp_sock(&ctrl_fd_id, ECOMMTCPPEERCHANGED, false, node_idx); + g_instance.comm_cxt.g_s_node_sock[node_idx].unlock(); + } + } + + return state; +} + +/* + * add local thread id to g_htab_tid_poll + * then thread will call gs_poll during connecting, send and recv + */ +int gs_poll_create() +{ + struct tid_entry* entry_tid = NULL; + bool found = false; + + if (t_thrd.comm_cxt.libcomm_semaphore != NULL) { + return 0; + } + + if (g_htab_tid_poll == NULL) { + errno = ECOMMTCPCVINIT; + LIBCOMM_ELOG(WARNING, "(libcomm tid lookup hash)\tg_htab_tid_poll is NULL."); + return -1; + } + +#ifdef LIBCOMM_FAULT_INJECTION_ENABLE + if (is_comm_fault_injection(LIBCOMM_FI_CREATE_POLL_FAILED)) { + errno = ECOMMTCPMEMALLOC; + LIBCOMM_ELOG(WARNING, "(poll create)\t[FAULT INJECTION]Failed to add local tid to g_htab_tid_poll."); + return -1; + } +#endif + + if (t_thrd.comm_cxt.MyPid <= 0) { + t_thrd.comm_cxt.MyPid = gettid(); + } + + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_tid_poll_lock); + entry_tid = (tid_entry*)hash_search(g_htab_tid_poll, &t_thrd.comm_cxt.MyPid, HASH_ENTER, &found); + if (!found) { + entry_tid->entry.val = -1; + entry_tid->entry._init(); + } + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_tid_poll_lock); + t_thrd.comm_cxt.libcomm_semaphore = &(entry_tid->entry.sem); + + return 0; +} + +// delete tid from tid_poll, usually called when thread exit or logic conn is closed. +// but when the thread needed to delete is calling gs_poll, just signal it instead of del it. +// because for CN, thread usually wait for multiple logic connection, so we cannot del it when +// some logic connection is close. +void gs_poll_close() +{ + AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); + if (t_thrd.comm_cxt.libcomm_semaphore != NULL) { + t_thrd.comm_cxt.libcomm_semaphore = NULL; + + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_tid_poll_lock); + hash_search(g_htab_tid_poll, &t_thrd.comm_cxt.MyPid, HASH_REMOVE, NULL); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_tid_poll_lock); + } + + return; +} + +/* + * thread which call gs_poll and block in here + * until the expected event happened + * or some error happened(timeout, logic conn is closed, interruption happened). + */ +int gs_poll(int time_out) +{ + return t_thrd.comm_cxt.libcomm_semaphore->timed_wait(time_out); +} + +/* + * siganl thread when the expected event happened or some error happened + */ +void gs_poll_signal(binary_semaphore* sem) +{ + if (sem != NULL) { + sem->post(); + } +} + +/* + * when recv some interruption, gs_auxiliary will + * signal all thread waitting in gs_poll + * and threads will check interruption. + */ +void gs_broadcast_poll() +{ + HASH_SEQ_STATUS hash_seq; + tid_entry* element = NULL; + + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_tid_poll_lock); + + hash_seq_init(&hash_seq, g_htab_tid_poll); + + while ((element = (tid_entry*)hash_seq_search(&hash_seq)) != NULL) { + element->entry._signal(); + } + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_tid_poll_lock); +} + +/* + * function name : gs_get_node_idx + * description : gs_get_node_idx gives the node idx of backend. + * the first node connected to current process is node idx 0. + * arguments : + * node_name: name of backend + * len: NAMEDATALEN + * return value : -1:failed + * >0:node id + */ +int gs_get_node_idx(char* node_name) +{ +#ifdef LIBCOMM_FAULT_INJECTION_ENABLE + if (is_comm_fault_injection(LIBCOMM_FI_NO_NODEIDX)) { + errno = ECOMMTCPINVALNODEID; + LIBCOMM_ELOG(WARNING, "(s|get nodeid)\t[FAULT INJECTION]Failed to obtain node id for node %s.", node_name); + return -1; + } +#endif + // get node index + struct nodename_entry* entry_name = NULL; + struct char_key ckey; + bool found = false; + errno_t ss_rc; + int ret = -1; + uint32 cpylen = comm_get_cpylen(node_name, NAMEDATALEN); + ss_rc = memset_s(ckey.name, NAMEDATALEN, 0x0, NAMEDATALEN); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s(ckey.name, NAMEDATALEN, node_name, cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + ckey.name[cpylen] = '\0'; + + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_nodename_node_idx_lock); + entry_name = (nodename_entry*)hash_search(g_htab_nodename_node_idx, &ckey, HASH_ENTER, &found); + + if (found) { + ret = entry_name->entry.val; + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock); + return ret; + } + + // if the node is not registed, get a node index and save node name -> node index to hash table + int node_idx = nodename_count + 1; + if (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) { + hash_search(g_htab_nodename_node_idx, &ckey, HASH_REMOVE, NULL); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock); + errno = ECOMMTCPINVALNODEID; + return -1; + } + + nodename_count++; + entry_name->entry.val = node_idx; + ret = entry_name->entry.val; + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock); + LIBCOMM_ELOG(LOG, "(s|get idx)\tGenerate node idx [%d] for node:%s.", ret, node_name); + + return ret; +} + +/* + * function name : gs_get_stream_id + * description : producer get usable stream index for current query, which is designed by StreamKey. + * if the key is already in the hash table, return it! + * arguments : + * _in_ key_ns: libcomm stream key with node index. + * return value : -1:failed + * >0:stream id + */ +int gs_get_stream_id(int node_idx) +{ +#ifdef LIBCOMM_FAULT_INJECTION_ENABLE + if (is_comm_fault_injection(LIBCOMM_FI_NO_STREAMID)) { + errno = ECOMMTCPSTREAMIDX; + LIBCOMM_ELOG(WARNING, + "(s|get sid)\t[FAULT INJECTION]Failed to obtain stream for node[%d]:%s.", + node_idx, + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename); + return -1; + } +#endif + + int streamid = -1; + + // have no usable stream id + if (g_instance.comm_cxt.g_usable_streamid[node_idx].pop( + g_instance.comm_cxt.g_usable_streamid + node_idx, &streamid) <= 0) { + errno = ECOMMTCPSTREAMIDX; + LIBCOMM_ELOG(WARNING, + "(s|get sid)\tFailed to obtain stream for node[%d]:%s, usable:%d/%d.", + node_idx, + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, + g_instance.comm_cxt.g_usable_streamid[node_idx].count, + g_instance.comm_cxt.counters_cxt.g_max_stream_num); + return -1; + } + + // succeed to return the entry in g_s_htab_nodeid_skey_to_stream + COMM_DEBUG_LOG("(s|get sid)\tObtain stream[%d] for node[%d]:%s.", + streamid, + node_idx, + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename); + + return streamid; +} // gs_r_get_usable_streamid + +int gs_update_fd_to_htab_socket_version(struct sock_id* fd_id) +{ + struct sock_ver_entry* entry_ver = NULL; + bool found = false; + int fd = fd_id->fd; + int id = fd_id->id; + + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock); + + entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_ENTER, &found); + if (!found) { + entry_ver->entry.val = id; + } else { // if there is an entry already, we update the version(id) of the socket(fd) + entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1); + fd_id->id = entry_ver->entry.val; // set the new id into fd_id !!! + COMM_DEBUG_LOG("(add fd & version)\tSucceed to update socket[%d] version[%d].", fd, fd_id->id); + } + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); + + return 0; +} // gs_update_fd_to_htab_socket_version + +// receiver close all streams of a node which is designed by control tcp socket +// we call this function because of the broken control tcp connection or tcp connection, +// if it is tcp connection, we should send the close info to remote +// step 1: get node index (node_idx) +// step 2: traverse g_c_mailbox[node_idx][*] +// step 3: do notify and reset all cmailbox +static void gs_r_close_all_streams_by_fd_idx(int fd, int node_idx, int close_reason) +{ + struct c_mailbox* cmailbox = NULL; + struct FCMSG_T fcmsgs = {0x0}; + // Note: we should have locked at the caller, so we need not lock here again + // + LIBCOMM_ELOG(WARNING, + "(r|close all streams)\tTo reset all streams " + "by socket[%d] for node[%d]:%s, detail:%s.", + fd, + node_idx, + g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename, + mc_strerror(close_reason)); + + for (int j = 1; j < g_instance.comm_cxt.counters_cxt.g_max_stream_num; j++) { + cmailbox = &C_MAILBOX(node_idx, j); + LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); + + if ((fd == -1 || cmailbox->ctrl_tcp_sock == fd) && (cmailbox->state != MAIL_CLOSED)) { + gs_r_close_logic_connection(cmailbox, close_reason, &fcmsgs); + // reset local stream logic connection info + COMM_DEBUG_LOG("(r|close all streams)\tTo close stream[%d], node[%d]:%s, query[%lu], socket[%d].", + j, + node_idx, + g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename, + cmailbox->query_id, + cmailbox->ctrl_tcp_sock); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + // Send close ctrl msg to remote without cmailbox lock + if (IS_NOTIFY_REMOTE(close_reason)) { + (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[node_idx], &fcmsgs, ROLE_CONSUMER); + } + } else { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + } + } +} // gs_r_reset_all_streams_by_fd_idx + +// sender close all streams of a node which is designed by control tcp socket +// we call this function because of the broken control tcp connection, so we did not need to send the status to remote +// step 1: get node index (node_idx) +// step 2: traverse g_p_mailbox[node_idx][*] +// step 3: do notification and reset all pmailbox +static void gs_s_close_all_streams_by_fd_idx(int fd, int node_idx, int close_reason, bool with_ctrl_lock) +{ + struct p_mailbox* pmailbox = NULL; + struct FCMSG_T fcmsgs = {0x0}; + // Note: we should have locked at the caller, so we need not lock here again + // + LIBCOMM_ELOG(WARNING, + "(s|close all streams)\tTo reset all streams by socket[%d] for node[%d]:%s, detail:%s.", + fd, + node_idx, + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, + mc_strerror(close_reason)); + + for (int j = 1; j < g_instance.comm_cxt.counters_cxt.g_max_stream_num; j++) { + pmailbox = &P_MAILBOX(node_idx, j); + LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock); + + if (((pmailbox->ctrl_tcp_sock == -1) || (fd == -1) || (pmailbox->ctrl_tcp_sock == fd)) && + (pmailbox->state != MAIL_CLOSED)) { + COMM_DEBUG_LOG("(s|close all streams)\tTo close stream[%d], node[%d]:%s, query[%lu], socket[%d].", + j, + node_idx, + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, + pmailbox->query_id, + pmailbox->ctrl_tcp_sock); + + gs_s_close_logic_connection(pmailbox, close_reason, &fcmsgs); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); + // Send close ctrl msg to remote without cmailbox lock + if (IS_NOTIFY_REMOTE(close_reason)) { + if (with_ctrl_lock) { + (void)gs_send_ctrl_msg_without_lock( + &g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, node_idx, ROLE_PRODUCER); + } else { + (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, ROLE_PRODUCER); + } + } + } else { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); + } + } +} // gs_s_close_all_streams_by_ctrl_tcp_sock + +/* To remove the closed fd in the pooler list. + * for example, if we have many events in the poll_list [poll_1, poll_2, poll_3,...], the poller at the rear may be + * closed by the front one. So we need to check and delete the closed one in the poll_list. + * Notice: if the g_libcomm_poller_list doesn't belong to the Caller, it just returns. + */ +static void gs_clean_events(struct sock_id* old_fd_id) +{ + int fd = -1; + int id = -1; + + if (t_thrd.comm_cxt.g_libcomm_poller_list == NULL) { + return; + } + int nevents = t_thrd.comm_cxt.g_libcomm_poller_list->nevents; + for (int i = 0; i < nevents; i++) { + fd = (int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64 >> MC_POLLER_FD_ID_OFFSET)); + id = (int)(((uint64)t_thrd.comm_cxt.g_libcomm_poller_list->events[i].data.u64 & MC_POLLER_FD_ID_MASK)); + if ((old_fd_id->fd == fd) && (old_fd_id->id == id)) { + COMM_DEBUG_LOG("(clean events)\tClean socket[%d,%d] in the poller list.", fd, id); + + /* if the old_fd_id in the poller list, we need to remove it. + * To simplify, we just move the last one to this position. + * if "i" is the last one, i == nevents-1, it doesn't matter. + * if ievents[i] = + t_thrd.comm_cxt.g_libcomm_poller_list->events[nevents - 1]; + t_thrd.comm_cxt.g_libcomm_poller_list->nevents--; + break; + } + } + + return; +} + +// receiver close and clear bad tcp control socket, and related information +void gs_r_close_bad_ctrl_tcp_sock(struct sock_id* fd_id, int close_reason) +{ + int fd = fd_id->fd; + int id = fd_id->id; + bool found = false; + + if (fd < 0 || id < 0) { + return; + } + + LIBCOMM_PTHREAD_MUTEX_LOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock); + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock); + + // step1: remove the fd from the poller cabinet + // + if (g_instance.comm_cxt.pollers_cxt.g_r_poller_list->del_fd(fd_id) != 0) { + LIBCOMM_ELOG(WARNING, + " (r|close tcp socket)\tFailed to delete socket[%d,%d] from poll list:%s.", + fd, + id, + mc_strerror(errno)); + } + + // step2: get node index by fd + // + int node_idx = -1; + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); + sock_id_entry* entry_id = (sock_id_entry*)hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_FIND, &found); + if (found) { + node_idx = entry_id->entry.val; + } + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); + // step3: make sure the fd and the version are matched, or it has been closed already + // + struct sock_ver_entry* entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_FIND, &found); + + if ((!found) || (entry_ver->entry.val != fd_id->id)) { + LIBCOMM_ELOG(WARNING, + "(r|close tcp socket)\tFailed to close socket[%d,%d], maybe already reused[%d,%d].", + fd, + id, + (found) ? fd : -1, + (found) ? entry_ver->entry.val : -1); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); + ; + LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock); + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); + hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); + return; + } + + LIBCOMM_ELOG(LOG, "(r|close bad tcp ctrl fds)\tClose bad socket with socket entry[%d,%d].", fd, id); + + if (node_idx >= 0) { + // step4: close all mailbox at receiver + // + gs_r_close_all_streams_by_fd_idx(fd_id->fd, node_idx, close_reason); + // step5: close the socket and reset the socket infomation structure(g_r_node_sock[node_idx]) + // + g_instance.comm_cxt.g_r_node_sock[node_idx].lock(); + if (g_instance.comm_cxt.g_r_node_sock[node_idx].ctrl_tcp_sock == fd_id->fd && + g_instance.comm_cxt.g_r_node_sock[node_idx].ctrl_tcp_sock_id == fd_id->id) { + g_instance.comm_cxt.g_r_node_sock[node_idx].close_socket_nl(CTRL_TCP_SOCK); + + LIBCOMM_ELOG(WARNING, + "(r|close tcp socket)\tTCP disconnect with socket[%d,%d] to host:%s, node[%d]:[%s].", + fd, + id, + g_instance.comm_cxt.g_r_node_sock[node_idx].remote_host, + node_idx, + g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename); + } + g_instance.comm_cxt.g_r_node_sock[node_idx].unlock(); + + gs_clean_events(fd_id); + } else { + mc_tcp_close(fd_id->fd); + } + // step6: if the fd is closed, we update the fd version + // + entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); + ; + LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_poller_list_lock); + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); + hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); + + return; +} // gs_r_close_bad_ctrl_tcp_sock + +// sender close and clear bad tcp control socket, and related information +// clean_epoll is true when this function is called by sender flow ctrl thread, +// we delete fd from epoll list and close fd. +// clean_epoll is false when this function is called by producer thread, +// in this case, we cannot close fd and delete from epoll list, +// cause other thread may use this fd after close, +// while sender flow control thread still use this fd to recv. +// NOTE: fd can be closed and deleted from epoll list only under the sender flow ctrl. +void gs_s_close_bad_ctrl_tcp_sock(struct sock_id* fd_id, int close_reason, bool clean_epoll, int node_idx) +{ + int fd = fd_id->fd; + int id = fd_id->id; + ip_key addr; + bool is_addr = false; + errno_t ss_rc; + uint32 cpylen; + bool found = false; + + if (fd < 0 || id < 0) { + return; + } + + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock); + // step1: remove the fd from the poller cabinet + // + if (clean_epoll) { + gs_clean_events(fd_id); + if (g_instance.comm_cxt.pollers_cxt.g_s_poller_list->del_fd(fd_id) != 0) { + COMM_DEBUG_LOG("(s|cls bad tcp socket)\tFailed to remove bad socket with socket entry[%d,%d]:%s.", + fd, + id, + mc_strerror(errno)); + } + } + + // step2: make sure the fd and the version are matched, or it has been closed already + // + struct sock_ver_entry* entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_FIND, &found); + if (!found || entry_ver->entry.val != fd_id->id) { + LIBCOMM_ELOG(WARNING, + "(s|cls bad tcp socket)\tFailed to close bad socket[%d,%d], socket entry[%d,%d].", + fd, + id, + (found) ? fd : -1, + (found) ? entry_ver->entry.val : -1); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); + return; + } + + // step3: close all mailbox at receiver + // + if (node_idx >= 0) { + gs_s_close_all_streams_by_fd_idx(fd_id->fd, node_idx, close_reason, true); + } + + // step4: close the socket and reset the socket infomation structure(g_s_node_sock[node_idx]) + // + LIBCOMM_ELOG(LOG, + "(s|close bad tcp ctrl fds)\tClose bad socket with socket entry[%d,%d] : %s.", + fd, + id, + mc_strerror(close_reason)); + if (node_idx >= 0) { + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); + hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); + + if (g_instance.comm_cxt.g_s_node_sock[node_idx].ctrl_tcp_sock == fd_id->fd && + g_instance.comm_cxt.g_s_node_sock[node_idx].ctrl_tcp_sock_id == fd_id->id) { + cpylen = comm_get_cpylen(g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, HOST_LEN_OF_HTAB); + ss_rc = memset_s(addr.ip, HOST_LEN_OF_HTAB, 0x0, HOST_LEN_OF_HTAB); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s( + addr.ip, HOST_LEN_OF_HTAB, g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + addr.ip[cpylen] = '\0'; + + addr.port = g_instance.comm_cxt.g_s_node_sock[node_idx].ctrl_tcp_port; + is_addr = true; + // producer thread detect destination ip is changed + // then notify the origination backend to close connection + if (close_reason == ECOMMTCPPEERCHANGED) { + struct FCMSG_T fcmsgs = {0x0}; + fcmsgs.type = CTRL_PEER_CHANGED; + fcmsgs.node_idx = node_idx; + fcmsgs.streamid = 1; + + cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); + ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s( + fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + fcmsgs.nodename[cpylen] = '\0'; + + (void)gs_send_ctrl_msg_without_lock( + &g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, node_idx, ROLE_PRODUCER); + } + g_instance.comm_cxt.g_s_node_sock[node_idx].set_nl(-1, CTRL_TCP_SOCK); + g_instance.comm_cxt.g_s_node_sock[node_idx].set_nl(-1, CTRL_TCP_SOCK_ID); + LIBCOMM_ELOG(WARNING, + "(s|cls bad tcp socket)\tClose bad socket[%d,%d] for host:%s, node[%d]:%s.", + fd, + id, + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, + node_idx, + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename); + } + } + + // clean_epoll is true only under sender flow control thread + if (clean_epoll) { + mc_tcp_close(fd_id->fd); + // step5: if the fd is closed, we update the fd version + // + entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1); + } + // step6: update connection state in htab + // + if (is_addr) { + gs_update_connection_state(addr, CONNSTATEFAIL, false, node_idx); + } + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); + +} // gs_s_close_bad_ctrl_tcp_sock + +int gs_memory_pool_queue_initial_success(uint32 index) +{ + return g_memory_pool_queue.initialize(index); +} + +struct mc_lqueue_item* gs_memory_pool_queue_pop(char* iov) +{ + return (struct mc_lqueue_item*)g_memory_pool_queue.pop(iov); +} + +bool gs_memory_pool_queue_push(char* item) +{ + return g_memory_pool_queue.push(item); +} + +// Calculation how many quota size need to add in mailbox +static long gs_add_quota_size(c_mailbox* cmailbox) +{ +#define COMM_HIGH_MEM_USED (used_memory >= (g_instance.comm_cxt.commutil_cxt.g_total_usable_memory * 0.8)) +#define COMM_MIDDLE_MEM_USED \ + (used_memory > (g_instance.comm_cxt.commutil_cxt.g_total_usable_memory * 0.5) && \ + used_memory < (g_instance.comm_cxt.commutil_cxt.g_total_usable_memory * 0.8)) +#define COMM_LOW_MEM_USED (used_memory <= (g_instance.comm_cxt.commutil_cxt.g_total_usable_memory * 0.5)) + + long used_memory = gs_get_comm_used_memory(); + long add_quota = 0; + long max_buff = 0; // must be equal [DEFULTMSGLEN, comm_quota_size] + long buff_used = cmailbox->buff_q->u_size; // the used buffer size in this mailbox + long old_quota = cmailbox->bufCAP; // the quota size in this mailbox + + used_memory -= g_memory_pool_queue.size() * IOV_ITEM_SIZE; + + // Calculate the maximum buffer size for this mailbox + if (COMM_HIGH_MEM_USED) { + max_buff = DEFULTMSGLEN; + } else if (COMM_LOW_MEM_USED) { + max_buff = (g_instance.comm_cxt.quota_cxt.g_quota > DEFULTMSGLEN) ? g_instance.comm_cxt.quota_cxt.g_quota + : DEFULTMSGLEN; + } else { // COMM_MIDDLE_MEM_USED + max_buff = (g_instance.comm_cxt.quota_cxt.g_quota / 8 > DEFULTMSGLEN) + ? g_instance.comm_cxt.quota_cxt.g_quota / 8 + : DEFULTMSGLEN; + } + + // because: max_buff = buff_used + old_quota + add_quota + // so: add_quota = max_buff - buff_used - old_quota + add_quota = max_buff - buff_used - old_quota; + + /* + * buff_used+old_quota is total data size that can be received when no send quota. + * if (buff_used+old_quota < g_quota/2), need send quota. + * if (buff_used+old_quota < DEFULTMSGLEN), need send quota. + */ + if ((buff_used + old_quota < (long)(g_instance.comm_cxt.quota_cxt.g_quota >> 1)) || + ((unsigned long)(buff_used + old_quota) < DEFULTMSGLEN)) { + return add_quota < 0 ? 0 : add_quota; + } else { + return 0; + } +} + +// auxiliary thread use it to change the stream state and send control message to remote point (sender) +bool gs_r_quota_notify(c_mailbox* cmailbox, FCMSG_T* msg) +{ + errno_t ss_rc; + uint32 cpylen; + int node_idx = cmailbox->idx; + int streamid = cmailbox->streamid; + unsigned long add_quota = gs_add_quota_size(cmailbox); + + if (add_quota > 0) { + // change local stream state and quota first + cmailbox->bufCAP += add_quota; + cmailbox->state = MAIL_RUN; + + COMM_DEBUG_LOG("(r|quota notify)\tSend quota to node[%d]:%s on stream[%d].", + node_idx, + g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename, + streamid); + + // send resume message to change remote stream state and quota + msg->type = CTRL_ADD_QUOTA; + msg->node_idx = cmailbox->idx; + msg->streamid = cmailbox->streamid; + msg->streamcap = add_quota; + msg->version = cmailbox->remote_version; + msg->query_id = cmailbox->query_id; + + cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); + ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + msg->nodename[cpylen] = '\0'; + + return true; + } + + return false; +} // gs_r_quota_notify + +// traverse all the c_mailbox(es) to find the first query who used memory, +// and make it failure to release the memory. Otherwise, the communication layer maybe hang up. +void gs_r_release_comm_memory() +{ + uint64 release_query_id = 0; + int nid = 0; + int sid = 1; + struct c_mailbox* cmailbox = NULL; + unsigned long buff_size = 0; + unsigned long total_buff_size = 0; + struct FCMSG_T fcmsgs = {0x0}; + + for (nid = 0; nid < g_instance.comm_cxt.counters_cxt.g_cur_node_num; nid++) { + for (sid = 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { + cmailbox = &C_MAILBOX(nid, sid); + LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); + + if (cmailbox->buff_q->u_size <= 0) { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + continue; + } + + // find the first query to release memory, save query id + if (release_query_id == 0) { + release_query_id = cmailbox->query_id; + } + + if (cmailbox->query_id == release_query_id) { + buff_size = cmailbox->buff_q->u_size; + total_buff_size += buff_size; + COMM_DEBUG_LOG("(r|release memory)\tReset stream[%d] on node[%d]:%s " + "for query[%lu] to release memory[%lu Byte].", + sid, + nid, + REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, nid), + release_query_id, + buff_size); + + gs_r_close_logic_connection(cmailbox, ECOMMTCPRELEASEMEM, &fcmsgs); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[nid], &fcmsgs, ROLE_CONSUMER); + } else { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + } + } + } + + LIBCOMM_ELOG(WARNING, + "(r|release memory)\tReset query[%lu] to release memory[%lu Byte].", + release_query_id, + total_buff_size); +} // gs_r_release_comm_memory + +// if we failed to receive message from a tcp listen socket, we should do following things +// step1: reset the streams of the related node +// step2: delete it from epoll cabinet +// step3: update the socket version +// step4: delete the socket from hash table socke -> node index (g_r_htab_data_socket_node_idx) +// step5: close the old tcp socket +void gs_r_close_bad_data_socket(int node_idx, sock_id fd_id, int close_reason, bool is_lock) +{ + if (node_idx >= 0) { + gs_r_close_all_streams_by_fd_idx(-1, node_idx, ECOMMTCPDISCONNECT); + if (is_lock) { + LIBCOMM_PTHREAD_RWLOCK_WRLOCK(&g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].rwlock); + } + g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].socket = -1; + if (is_lock) { + LIBCOMM_PTHREAD_RWLOCK_UNLOCK(&g_instance.comm_cxt.g_receivers->receiver_conn[node_idx].rwlock); + } + } + + bool found = false; + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock); + struct sock_ver_entry* entry_ver = + (sock_ver_entry*)hash_search(g_htab_socket_version, &fd_id.fd, HASH_FIND, &found); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); + + if (!found || entry_ver->entry.val != fd_id.id) { + LIBCOMM_ELOG(WARNING, + "(r|close bad data socket)\tFailed to close bad socket[%d,%d], socket entry[%d,%d].", + fd_id.fd, + fd_id.id, + (found) ? fd_id.fd : -1, + (found) ? entry_ver->entry.val : -1); + return; + } + + bool is_delete = false; + + LIBCOMM_PTHREAD_MUTEX_LOCK(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock); + + /* try to delete old_fd in g_libcomm_receiver_poller_list. + * because we have several recv thread, if the old_fd_id belongs to this thread, it can delete it successfully, + * otherwise, it returns false. + */ + if (t_thrd.comm_cxt.g_libcomm_recv_poller_hndl_list != NULL) { + is_delete = (t_thrd.comm_cxt.g_libcomm_recv_poller_hndl_list->del_fd(&fd_id) == 0) ? true : false; + } + LIBCOMM_PTHREAD_MUTEX_UNLOCK(g_instance.comm_cxt.pollers_cxt.g_r_libcomm_poller_list_lock); + + /* del fd_id in the htab, + * next time, -1 = g_htab_fd_id_node_idx.get_value(fd_id), So we needn't to gs_r_close_all_streams again. + */ + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); + hash_search(g_htab_fd_id_node_idx, &fd_id, HASH_REMOVE, NULL); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); + + if (is_delete) { + // if the old_fd_id belongs to this recv thread, we need to clean it in the poll list, and then close(fd). + gs_clean_events(&fd_id); + if (gs_update_fd_to_htab_socket_version(&fd_id) < 0) { + LIBCOMM_ELOG( + WARNING, "(r|close bad data socket)\tFailed to update bad data socket[%d,%d].", fd_id.fd, fd_id.id); + } + mc_tcp_close(fd_id.fd); + } else { + /* if the old_fd_id belongs to other recv thread, it means, the old_fd_id isn't in this poll_list, + * So we needn't to gs_clean_events(). we just use shutdown to send notification signal. + */ + shutdown(fd_id.fd, SHUT_RDWR); + COMM_DEBUG_LOG("(r|close bad data socket)\tSend shutdown signal for [%d,%d].", fd_id.fd, fd_id.id); + } +} + +// if we failed to send message to the destination, we should do following things +void gs_s_close_bad_data_socket(struct sock_id* fd_id, int close_reason, int node_idx) +{ + errno_t ss_rc; + uint32 cpylen; + int fd = fd_id->fd; + int id = fd_id->id; + ip_key addr; + bool is_addr = false; + bool found = false; + + if ((fd_id->fd < 0) || (fd_id->id < 0)) { + return; + } + + // step1: make sure the fd and the version are matched, or it has been closed already + // + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_socket_version_lock); + struct sock_ver_entry* entry_ver = (sock_ver_entry*)hash_search(g_htab_socket_version, &fd, HASH_FIND, &found); + + if (!found) { + mc_tcp_close(fd_id->fd); + } + + if (!found || entry_ver->entry.val != fd_id->id) { + LIBCOMM_ELOG(WARNING, + "(s|cls bad data socket)\tFailed to close bad socket[%d,%d], socket entry[%d,%d].", + fd, + id, + (found) ? fd : -1, + (found) ? entry_ver->entry.val : -1); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); + hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); + + return; + } + + // step2: close the bad socket + // + LIBCOMM_ELOG(LOG, "(s|cls bad data socket)\tClose bad socket with socket entry[%d,%d].", fd, id); + + if (node_idx >= 0) { + /* reset the socket for sender, unexpected case if this condition mismatch */ + if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket == fd_id->fd && + g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id == fd_id->id) { + + cpylen = + comm_get_cpylen(g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host, HOST_LEN_OF_HTAB); + ss_rc = memset_s(addr.ip, HOST_LEN_OF_HTAB, 0x0, HOST_LEN_OF_HTAB); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s(addr.ip, + HOST_LEN_OF_HTAB, + g_instance.comm_cxt.g_senders->sender_conn[node_idx].remote_host, + cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + addr.ip[cpylen] = '\0'; + + addr.port = g_instance.comm_cxt.g_senders->sender_conn[node_idx].port; + is_addr = true; + + mc_tcp_close(g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket); + g_instance.comm_cxt.g_senders->sender_conn[node_idx].port = -1; + g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket = -1; + g_instance.comm_cxt.g_senders->sender_conn[node_idx].socket_id = -1; + g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id = 0; + LIBCOMM_ELOG(WARNING, + "(s|cls bad data socket)\tClose bad data socket with socket entry[%d,%d] " + "to host:%s, node[%d], node name[%s]:%s.", + fd, + id, + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host, + node_idx, + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, + mc_strerror(errno)); + } + } else { + mc_tcp_close(fd_id->fd); + } + // step3: update connection state in htab + // + if (is_addr) { + gs_update_connection_state(addr, CONNSTATEFAIL, false, node_idx); + } + + // step4: if the bad socket is closed, we update the fd version + // + entry_ver->entry.val = (entry_ver->entry.val == MAX_FD_ID) ? 0 : (entry_ver->entry.val + 1); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_socket_version_lock); + + /* + * close all p_mailbox[node_idx][*] + * without g_htab_socket_version lock + * with g_instance.comm_cxt.g_senders->sender_conn[node_idx].rwlock + */ + if (node_idx >= 0) { + gs_s_close_all_streams_by_fd_idx(-1, node_idx, close_reason, false); + } + + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_fd_id_node_idx_lock); + hash_search(g_htab_fd_id_node_idx, &(*fd_id), HASH_REMOVE, NULL); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_fd_id_node_idx_lock); + + return; +} // gs_s_close_bad_data_socket + +/* + * @Description: push the data package to cmailbox buffer. + * @IN cmailbox: point of cmailbox. + * @IN iov: data package. + * @Return: -1: push data failed. + * 0: push data succsessed. + * @See also: + */ +int gs_push_cmailbox_buffer(c_mailbox* cmailbox, struct mc_lqueue_item* q_item, int version) +{ + struct iovec* iov = q_item->element.data; + COMM_TIMER_INIT(); + + int sid = cmailbox->streamid; + int idx = cmailbox->idx; + uint64 signal_start = 0; + uint64 signal_end = 0; + uint64 time_now = 0; + + LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); + + // if the stream is closed or ready to close, the data should be dropped. + if (false == gs_check_mailbox(cmailbox->local_version, version)) { + COMM_DEBUG_LOG("(r|inner recv)\tStream[%d] is closed for node[%d]:%s, drop reveived message[%d].", + sid, + idx, + g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename, + (int)iov->iov_len); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + errno = cmailbox->close_reason; + return -1; + } + + DEBUG_QUERY_ID = cmailbox->query_id; + + // there is buffer/quota to process the received data + if (cmailbox->bufCAP >= (unsigned long)(iov->iov_len)) { + COMM_DEBUG_LOG("(r|inner recv)\tNode[%d]:%s stream[%d] recv %zu msg:%c, bufCAP[%lu] and buff_q->u_size[%lu].", + idx, + g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename, + sid, + iov->iov_len, + ((char*)iov->iov_base)[0], + cmailbox->bufCAP, + cmailbox->buff_q->u_size); + + // put the message to the buffer in the c_mailbox + (void)mc_lqueue_add(cmailbox->buff_q, q_item); + + if (g_instance.comm_cxt.quota_cxt.g_having_quota) { + cmailbox->bufCAP -= iov->iov_len; + } + + signal_start = COMM_STAT_TIME(); + // wake up the Consumer thread of executor, to notify Consumer of arriving new message + gs_poll_signal(cmailbox->semaphore); + + COMM_TIMER_LOG("(r|inner recv)\tCache data from node[%d]:%s stream[%d].", + idx, + g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename, + sid); + } else { // there is no buffer/quota to process the received data, it should not happen + LIBCOMM_ELOG(WARNING, + "(r|inner recv)\tNode[%d] stream[%d], node name[%s] has bufCAP[%lu] and got[%d].", + idx, + sid, + g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename, + cmailbox->bufCAP, + (int)iov->iov_len); + LIBCOMM_ASSERT(false, idx, sid, ROLE_CONSUMER); + } + + /* update the statistic information of the mailbox */ + if (cmailbox->statistic != NULL) { + time_now = COMM_STAT_TIME(); + if (cmailbox->statistic->first_recv_time == 0) { + cmailbox->statistic->first_recv_time = time_now; + } + signal_end = time_now; + cmailbox->statistic->total_signal_time += ABS_SUB(signal_end, signal_start); + cmailbox->statistic->last_recv_time = time_now; + cmailbox->statistic->recv_bytes += iov->iov_len; + cmailbox->statistic->recv_loop_time += ABS_SUB(time_now, t_thrd.comm_cxt.g_receiver_loop_poll_up); + cmailbox->statistic->recv_loop_count++; + } + + if (cmailbox->bufCAP < DEFULTMSGLEN) { + cmailbox->state = MAIL_HOLD; + } + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + + return 0; +} + +int gs_handle_data_delay_message(int idx, struct mc_lqueue_item* q_item, uint16 msg_type) +{ + struct c_mailbox* cmailbox = NULL; + struct libcomm_delay_package* delay_msg = NULL; + struct iovec* iov = q_item->element.data; + + if (idx < 0) { + return -1; + } + + delay_msg = (struct libcomm_delay_package*)iov->iov_base; + + if (msg_type == LIBCOMM_PKG_TYPE_DELAY_REQUEST) { + delay_msg->recv_time = (uint32)mc_timers_us(); + } else if (msg_type == LIBCOMM_PKG_TYPE_DELAY_REPLY) { + delay_msg->finish_time = (uint32)mc_timers_us(); + } + + // put the message to the buffer in the c_mailbox + cmailbox = &C_MAILBOX(idx, 0); + LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); + (void)mc_lqueue_add(cmailbox->buff_q, q_item); + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + + return 0; +} + +/* + * function name : gs_s_close_logic_connection + * description : producer close logic connetion and reset mailbox, + * if producer call this, we only set state is CTRL_TO_CLOSE, + * then really close when consumer send MAIL_CLOSED message. + * notice : we must get mailbox lock before + * arguments : _in_ cmailbox: libcomm logic conntion info. + * _in_ close_reason: close reason. + */ +void gs_s_close_logic_connection(struct p_mailbox* pmailbox, int close_reason, FCMSG_T* msg) +{ + errno_t ss_rc; + uint32 cpylen; + + if (pmailbox->state == MAIL_CLOSED) { + return; + } + + // when sender closes pmailbox, if close reason != remote close, and the state of pmailbox is MAIL_READY, + // we set pmailbox to MAIL_TO_CLOSE, then wait for top consumer(gs_connect()) to close it. + if ((pmailbox->state == MAIL_READY) && (close_reason != ECOMMTCPREMOETECLOSE)) { + pmailbox->state = MAIL_TO_CLOSE; + // wake up the producer who is waiting + gs_poll_signal(pmailbox->semaphore); + pmailbox->semaphore = NULL; + return; + } + + // 1, if tcp disconnect, we can not send control message on tcp channel, + // remote can receive disconnect event when flow control thread call epoll_wait. + // 2, close reason is ECOMMTCPREMOETECLOSE means remote send MAIL_CLOSED, + // we could not reply MAIL_CLOSED message. + if (IS_NOTIFY_REMOTE(close_reason) && msg) { + msg->type = CTRL_CLOSED; + msg->node_idx = pmailbox->idx; + msg->streamid = pmailbox->streamid; + msg->streamcap = 0; + msg->version = pmailbox->remote_version; + msg->query_id = pmailbox->query_id; + + cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); + ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + msg->nodename[cpylen] = '\0'; + } + + // wake up the producer who is waiting + gs_poll_signal(pmailbox->semaphore); + + // At last, reset mailbox and clean hash table + gs_s_reset_pmailbox(pmailbox, close_reason); + + return; +} + +// send assert fail msg via ctrl connection if debug mode enable and assert failed +// +void gs_libcomm_handle_assert(bool condition, int nidx, int sidx, int node_role) +{ + errno_t ss_rc; + uint32 cpylen; + + if (mc_unlikely(!condition)) { + struct FCMSG_T fcmsgs = {0x0}; + // notify peer assertion failed + fcmsgs.type = CTRL_ASSERT_FAIL; + fcmsgs.node_idx = nidx; + fcmsgs.streamid = sidx; + + cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); + ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s(fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + fcmsgs.nodename[cpylen] = '\0'; + + if (node_role == ROLE_PRODUCER) { + (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[nidx], &fcmsgs, node_role); + struct p_mailbox* pmailbox = NULL; + pmailbox = &(P_MAILBOX(nidx, sidx)); + LIBCOMM_ELOG(WARNING, + "(s|handle assert)\tNode[%d] stream[%d] assert fail, node name[%s] with state[%d] has bufCAP[%lu].", + nidx, + sidx, + g_instance.comm_cxt.g_s_node_sock[nidx].remote_nodename, + pmailbox->state, + pmailbox->bufCAP); + MAILBOX_ELOG(pmailbox, WARNING, "(s|handle assert)\tMailbox Info which assert fail."); + } else { + (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[nidx], &fcmsgs, node_role); + struct c_mailbox* cmailbox = NULL; + cmailbox = &(C_MAILBOX(nidx, sidx)); + LIBCOMM_ELOG(WARNING, + "(r|handle assert)\tNode[%d] stream[%d] assert fail, node name[%s] with state[%d] has bufCAP[%lu] and " + "buff_q->u_size[%lu].", + nidx, + sidx, + g_instance.comm_cxt.g_r_node_sock[nidx].remote_nodename, + cmailbox->state, + cmailbox->bufCAP, + cmailbox->buff_q->u_size); + MAILBOX_ELOG(cmailbox, WARNING, "(r|handle assert)\tMailbox Info which assert fail."); + } + Assert(condition); + } +} + +/* + * function name : gs_s_build_reply_conntion + * description : as the connection between cn & dn is duplex. + * cn need to inital cmailbox to recv msgs from dn + * arguments : fcmsgr: provides gs_sock + */ +static void gs_s_build_reply_conntion(libcommaddrinfo* addr_info, int remote_version) +{ + int node_idx = addr_info->gs_sock.idx; + int streamid = addr_info->gs_sock.sid; + int local_version = addr_info->gs_sock.ver; + + // initialize consumer cmailbox + struct c_mailbox* cmailbox = &C_MAILBOX(node_idx, streamid); + LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); + + if (gs_check_mailbox(cmailbox->local_version, local_version) == true) { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + return; + } + + if (cmailbox->state != MAIL_CLOSED) { + MAILBOX_ELOG(cmailbox, + WARNING, + "(s|build reply conn)\tFailed to get mailbox for node[%d,%d]:%s.", + node_idx, + streamid, + REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx)); + gs_r_close_logic_connection(cmailbox, ECOMMTCPREMOETECLOSE, NULL); + } + + cmailbox->local_version = local_version; + cmailbox->remote_version = remote_version; + cmailbox->ctrl_tcp_sock = g_instance.comm_cxt.g_r_node_sock[node_idx].ctrl_tcp_sock; + cmailbox->state = MAIL_RUN; + cmailbox->bufCAP = DEFULTMSGLEN; + cmailbox->stream_key = addr_info->streamKey; + cmailbox->query_id = DEBUG_QUERY_ID; + cmailbox->local_thread_id = 0; + cmailbox->peer_thread_id = 0; + cmailbox->close_reason = 0; + if (g_instance.comm_cxt.commutil_cxt.g_stat_mode && (cmailbox->statistic == NULL)) { + LIBCOMM_MALLOC(cmailbox->statistic, sizeof(struct cmailbox_statistic), cmailbox_statistic); + if (NULL == cmailbox->statistic) { + errno = ECOMMTCPRELEASEMEM; + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + return; + } + } + COMM_STAT_CALL(cmailbox, cmailbox->statistic->start_time = (uint32)mc_timers_ms()); + COMM_DEBUG_LOG("(s|build reply conn)\tNode[%d] stream[%d], node name[%s] is in state[%s].", + node_idx, + streamid, + g_instance.comm_cxt.g_r_node_sock[node_idx].remote_nodename, + stream_stat_string(cmailbox->state)); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + + return; +} + +/* + * check all mailboxs state is MAIL_READY + * if the state of mailbox is MAIL_TO_CLOSE, top consumer will close the logic connection. + * if mailbox state is CTRL_CLOSE(gs_check_mailbox return false), + * means consumer do not need the data from this datanode, not report error + */ +int gs_check_all_mailbox(libcommaddrinfo** libcomm_addrinfo, int addr_num, int re, + bool TempImmediateInterruptOK, int timeout) +{ + int wait_index; + int i; + int node_idx = -1; + int streamid = -1; + int version = -1; + int remote_version = -1; + int error_index = -1; + bool build_reply_conn = false; + libcommaddrinfo* addr_info = NULL; + struct p_mailbox* pmailbox = NULL; + + for (;;) { + wait_index = -1; + for (i = 0; i < addr_num; i++) { + addr_info = libcomm_addrinfo[i]; + node_idx = addr_info->gs_sock.idx; + streamid = addr_info->gs_sock.sid; + version = addr_info->gs_sock.ver; + build_reply_conn = false; + + pmailbox = &P_MAILBOX(node_idx, streamid); + LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock); + // if gs_check_mailbox return false, means consumer close it, not need report error + if (gs_check_mailbox(pmailbox->local_version, version) == true) { + if (pmailbox->state == MAIL_READY) { + pmailbox->semaphore = t_thrd.comm_cxt.libcomm_semaphore; + COMM_DEBUG_LOG( + "(s|parallel connect)\tWait node[%d] stream[%d] state[%s], node name[%s], bufCAP[%lu].", + node_idx, + streamid, + stream_stat_string(pmailbox->state), + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename, + pmailbox->bufCAP); + wait_index = i; + } else if (pmailbox->state == MAIL_TO_CLOSE) { // mail would close later, sender closes pmailbox, + LIBCOMM_ELOG(WARNING, + "(s|parallel connect)\tMAIL_TO_CLOSE node[%d] stream[%d] state[%s], node name[%s].", + node_idx, + streamid, + stream_stat_string(pmailbox->state), + g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename); + gs_s_close_logic_connection(pmailbox, ECOMMTCPREMOETECLOSE, NULL); + + // before continue or goto clean_connection, we must release the sinfo_lock. + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); + if (IS_PGXC_COORDINATOR) { + addr_info->gs_sock = GS_INVALID_GSOCK; + continue; + } else { + errno = ECOMMTCPCONNFAIL; + error_index = i; + return gs_clean_connection(libcomm_addrinfo, addr_num, error_index, + re, TempImmediateInterruptOK); + } + } else { + pmailbox->semaphore = NULL; + // for cn initial cmailbox as well as the connection is duplex + if (IS_PGXC_COORDINATOR) { + remote_version = pmailbox->remote_version; + build_reply_conn = true; + } + } + } else { + // if gs_check_mailbox return false, means consumer close it, we need to reset gsockt + addr_info->gs_sock = GS_INVALID_GSOCK; + } + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); + + // for cn initial cmailbox as well as the connection is duplex + if (build_reply_conn) { + // build cmailbox with the same version and remote_verion as pmailbox, + // when this connection is duplex. + gs_s_build_reply_conntion(addr_info, remote_version); + } + } + + // we wait on the last mailbox that state is not MAIL_READY + if (wait_index >= 0) { + pgstat_report_waitstatus_comm(STATE_STREAM_WAIT_CONNECT_NODES, + libcomm_addrinfo[wait_index]->nodeIdx, + wait_index + 1, + -1, + global_node_definition ? global_node_definition->num_nodes : -1); + + re = gs_poll(timeout); + if (re == ETIMEDOUT) { + if (IS_PGXC_COORDINATOR) { + /* close all timeout connections */ + gs_close_timeout_connections(libcomm_addrinfo, addr_num, node_idx, streamid); + } else { + errno = ETIMEDOUT; + error_index = wait_index; + return gs_clean_connection(libcomm_addrinfo, addr_num, error_index, + re, TempImmediateInterruptOK); + } + } + } else { + break; + } + } + + return 0; +} + +/* + * function name : gs_s_send_start_ctrl_msg + * description : producer send local thread id to consumer, + * it is means producer start to send data. + * notice : we must get mailbox lock before. + * arguments : + * _in_ pmailbox: logic conntion info. + * return value : + * false: failed. + * true : succeed. + */ +bool gs_s_form_start_ctrl_msg(p_mailbox* pmailbox, FCMSG_T* msg) +{ + pid_t local_tid = t_thrd.comm_cxt.MyPid; + errno_t ss_rc; + uint32 cpylen; + + if (pmailbox->query_id != DEBUG_QUERY_ID) { + pmailbox->query_id = DEBUG_QUERY_ID; + } + + // send local thread id to remote + if (local_tid != pmailbox->local_thread_id) { + int node_idx = pmailbox->idx; + int streamid = pmailbox->streamid; + + // change local stream state and quota first + pmailbox->local_thread_id = local_tid; + + // change local stream state and quota first + msg->type = CTRL_PEER_TID; + msg->node_idx = node_idx; + msg->streamid = streamid; + msg->streamcap = 0; + msg->version = pmailbox->remote_version; + msg->extra_info = pmailbox->local_thread_id; + msg->query_id = pmailbox->query_id; + + cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); + ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + msg->nodename[cpylen] = '\0'; + + return true; + } + + return false; +} // gs_s_send_start_ctrl_msg + +/* + * @Description: push the data package to local cmailbox buffer. + * @IN streamid: the producer and consumer have the same stream id. + * @IN message: data message. + * @IN m_len: message len. + * @Return: -1: push data failed. + * m_len: push data succsessed. + * @See also: local producer can use memcpy to push data package, + * no need push to data stack + */ +int gs_push_local_buffer(int streamid, const char* message, int m_len, int cmailbox_version) +{ + int cmailbox_idx = -1; + struct char_key ckey; + c_mailbox* cmailbox = NULL; + errno_t ss_rc; + bool found = false; + uint32 cpylen; + + t_thrd.comm_cxt.g_receiver_loop_poll_up = COMM_STAT_TIME(); + + cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); + ss_rc = memset_s(ckey.name, NAMEDATALEN, 0x0, NAMEDATALEN); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s(ckey.name, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + ckey.name[cpylen] = '\0'; + + LIBCOMM_PTHREAD_MUTEX_LOCK(&g_htab_nodename_node_idx_lock); + nodename_entry* entry_name = (nodename_entry*)hash_search(g_htab_nodename_node_idx, &ckey, HASH_FIND, &found); + if (found) { + cmailbox_idx = entry_name->entry.val; + } + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&g_htab_nodename_node_idx_lock); + + if (cmailbox_idx < 0) { + errno = ECOMMTCPREMOETECLOSE; + return -1; + } + cmailbox = &C_MAILBOX(cmailbox_idx, streamid); + + struct iovec* iov = NULL; + struct mc_lqueue_item* iov_item = NULL; + // use share memory malloc for buffer received data message + if (libcomm_malloc_iov_item(&iov_item, IOV_DATA_SIZE) != 0) { + return -1; + } + iov = iov_item->element.data; + + // copy the datat to the buffer of executor + ss_rc = memcpy_s(iov->iov_base, IOV_DATA_SIZE, message, m_len); + securec_check(ss_rc, "\0", "\0"); + iov->iov_len = m_len; + + if (gs_push_cmailbox_buffer(cmailbox, iov_item, cmailbox_version) < 0) { + libcomm_free_iov_item(&iov_item, IOV_DATA_SIZE); + return -1; + } + + /* + * This process is invoked when a DN sends a message to itself. + * When the libcomm sends data to the local node, the data is directly inserted into the memory + * and needs to be proactively notified to the listener. + */ + if (ENABLE_THREAD_POOL_DN_LOGICCONN) { + NotifyListener(cmailbox, false, __FUNCTION__); + } + return m_len; +} + +/* + * function name : gs_r_send_start_ctrl_msg + * description : consumer send local thread id to producer, + * it is means consumer start to receive data, + * so we also send quota to producer. + * notice : we must get mailbox lock before. + * arguments : + * _in_ cmailbox: logic conntion info. + * return value : + * false: failed. + * true : succeed. + */ +static bool gs_r_form_start_ctrl_msg(c_mailbox* cmailbox, FCMSG_T* msg) +{ + pid_t local_tid = t_thrd.comm_cxt.MyPid; + errno_t ss_rc; + uint32 cpylen; + + if (cmailbox->query_id != DEBUG_QUERY_ID) { + cmailbox->query_id = DEBUG_QUERY_ID; + } + + // send local thread id and quota to remote + if (local_tid != cmailbox->local_thread_id) { + int node_idx = cmailbox->idx; + int streamid = cmailbox->streamid; + long add_quota = 0; + + if (g_instance.comm_cxt.quota_cxt.g_having_quota) { + add_quota = gs_add_quota_size(cmailbox); + } + + // change local stream state and quota first + cmailbox->local_thread_id = local_tid; + cmailbox->bufCAP += add_quota; + cmailbox->state = MAIL_RUN; + + // then change remote stream state and quota + msg->type = CTRL_PEER_TID; + msg->node_idx = node_idx; + msg->streamid = streamid; + msg->streamcap = add_quota; + msg->version = cmailbox->remote_version; + msg->extra_info = cmailbox->local_thread_id; + msg->query_id = cmailbox->query_id; + + cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); + ss_rc = memset_s(msg->nodename, NAMEDATALEN, 0x0, NAMEDATALEN); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s(msg->nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + msg->nodename[cpylen] = '\0'; + + return true; + } + + return false; +} // gs_r_send_start_ctrl_msg + +static void update_cmailbox_statistic(struct c_mailbox* cmailbox, check_cmailbox_option opt, int n_got_data) +{ + libcomm_time_record* time_record = opt.time_record; + if (cmailbox->statistic != NULL) { + time_record->wait_data_time = ABS_SUB(time_record->wait_data_end, time_record->wait_data_start); + cmailbox->statistic->wait_data_time += time_record->wait_data_time; + + time_record->time_now = COMM_STAT_TIME(); + cmailbox->statistic->wait_lock_time += ABS_SUB(time_record->time_now, time_record->wait_lock_start); + + if (cmailbox->statistic->first_poll_time == 0) { + cmailbox->statistic->first_poll_time = time_record->time_enter; + cmailbox->statistic->consumer_elapsed_time += + ABS_SUB(time_record->time_enter, cmailbox->statistic->start_time); + } else { + cmailbox->statistic->consumer_elapsed_time += + ABS_SUB(time_record->time_enter, t_thrd.comm_cxt.g_consumer_process_duration); + } + + if (opt.first_cycle) { + cmailbox->statistic->call_poll_count++; + cmailbox->statistic->last_poll_time = time_record->time_enter; + } + + if (n_got_data > 0 || opt.poll_error_flag == 1) { + cmailbox->statistic->total_poll_time += ABS_SUB(time_record->time_now, time_record->time_enter); + } + } +} + +static void gs_update_producer(struct c_mailbox* cmailbox, int* producer, int* n_got_data, int poll_error_flag) +{ + // there is data in the mailbox already + if (cmailbox->buff_q->count > 0) { + (*n_got_data)++; + // set the having label for Consumer thread + *producer = WAIT_POLL_FLAG_GOT; + } + // need return + if (*n_got_data > 0 || poll_error_flag == 1) { + /* gs_wait_poll will return, clean semaphore */ + cmailbox->semaphore = NULL; + if (*producer == WAIT_POLL_FLAG_WAIT) { + *producer = WAIT_POLL_FLAG_IDLE; + } + } else { + /* gs_wait_poll will enter gs_poll, regist semaphore */ + cmailbox->semaphore = t_thrd.comm_cxt.libcomm_semaphore; + *producer = WAIT_POLL_FLAG_WAIT; + } +} + +// check if there is data in the c_mailbox of the given node index and stream index already +int gs_check_cmailbox_data(const gsocket* gs_sock_array, // array of producers node index + int nproducer, // number of producers + int* producer, // producers number triggers poll + bool close_expected, // is logic connection closed by remote is an expected result + check_cmailbox_option opt) +{ + int n_got_data = 0; + int idx = -1; + int streamid = -1; + int version = -1; + libcomm_time_record* time_record = opt.time_record; + COMM_TIMER_COPY_INIT(time_record->t_begin); + struct c_mailbox* cmailbox = NULL; + struct FCMSG_T fcmsgs = {0x0}; + for (int i = 0; i < nproducer; i++) { + idx = gs_sock_array[i].idx; + streamid = gs_sock_array[i].sid; + version = gs_sock_array[i].ver; + Assert(idx >= 0 && idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num && streamid > 0 && + streamid < g_instance.comm_cxt.counters_cxt.g_max_stream_num); + + // get the cmailbox then lock it + cmailbox = &C_MAILBOX(idx, streamid); + LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); + + // check the state of the mailbox is correct + // ret -2 means close by remote + if (false == gs_check_mailbox(cmailbox->local_version, version)) { + if (!close_expected) { + MAILBOX_ELOG(cmailbox, WARNING, "(r|wait poll)\tStream has already closed, detail:%s.", + mc_strerror(cmailbox->close_reason)); + } + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + errno = cmailbox->close_reason; + // set the error flag for Consumer thread + producer[i] = WAIT_POLL_FLAG_ERROR; + return -2; + } + + gs_update_producer(cmailbox, &producer[i], &n_got_data, opt.poll_error_flag); + + /* update the statistic information of the mailbox */ + update_cmailbox_statistic(cmailbox, opt, n_got_data); + + // send local thread id and quota to remote + bool send_msg = gs_r_form_start_ctrl_msg(cmailbox, &fcmsgs); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + + // send local thread id and quota to remote without cmailbox lock + if (send_msg && (gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[idx], &fcmsgs, ROLE_CONSUMER) <= 0)) { + errno = ECOMMTCPTCPDISCONNECT; + return -1; + } + } + // if data is found, return the number of mailboxes which have data + // or, if it is waked up here but no data found, there must be interruption, we should return and + // CHECK_FOR_INTERRUPT + // + if (n_got_data > 0) { + COMM_TIMER_LOG("(r|wait poll)\tGet data for node[%d,%d]:%s.", idx, streamid, + REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, idx)); + return n_got_data; + } else if (opt.poll_error_flag == 1) { + COMM_DEBUG_LOG("(r|wait poll)\tWaked up but no data."); + errno = ECOMMTCPWAITPOLLERROR; + return -1; + } + return 0; +} + +void gs_check_all_producers_mailbox(const gsocket* gs_sock_array, int nproducer, int* producer) +{ + struct c_mailbox* cmailbox = NULL; + for (int i = 0; i < nproducer; i++) { + if (producer[i] == WAIT_POLL_FLAG_WAIT) { + producer[i] = WAIT_POLL_FLAG_IDLE; + cmailbox = &C_MAILBOX(gs_sock_array[i].idx, gs_sock_array[i].sid); + LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); + if (gs_check_mailbox(cmailbox->local_version, gs_sock_array[i].ver) == true) { + cmailbox->semaphore = NULL; + } + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + } + } +} + +/* Handle the same message and print message receiving and sending log. */ +void gs_comm_ipc_print(MessageIpcLog *ipc_log, char *remotenode, gsocket *gs_sock, CommMsgOper msg_oper) +{ + /* if the same number of messages is greater than 1, print this message. */ + if (ipc_log->last_msg_count > 1) { + ereport(LOG, + (errmsg("(%s) comm_ipc_log, msgtype:%c, total_len:%d, msg_count:%d, node:%s, last msg time:%s.", + msg_oper_string(msg_oper), ipc_log->last_msg_type, ipc_log->last_msg_len, + ipc_log->last_msg_count, remotenode, ipc_log->last_msg_time))); + } + + /* print current message */ + if ((gs_sock != NULL && gs_sock->idx == 0 && gs_sock->sid == 0) || gs_sock == NULL) { + ereport(LOG, (errmsg("(%s) comm_ipc_log, msgtype:%c, len:%d, node:%s.", + msg_oper_string(msg_oper), ipc_log->type, ipc_log->msg_len, remotenode))); + } else { + ereport(LOG, (errmsg("(%s) comm_ipc_log, msgtype:%c, len:%d, node:%s[nid:%d,sid:%d].", + msg_oper_string(msg_oper), ipc_log->type, ipc_log->msg_len, + remotenode, gs_sock->idx, gs_sock->sid))); + } +} + +// cancel request for receiver (called by die() or StatementCancelHandler() in postgresMain ) +void gs_r_cancel() +{ + // use g_cancel_requested save DEBUG_QUERY_ID as a flag + g_instance.comm_cxt.reqcheck_cxt.g_cancel_requested = + (t_thrd.proc_cxt.MyProcPid != 0) ? t_thrd.proc_cxt.MyProcPid : 1; +} + +// receiver close logic stream, call by Consumer thread +// when no data need or all data are received, or error happed +int gs_r_close_stream(gsocket* gsock) +{ + int node_idx = gsock->idx; + int stream_idx = gsock->sid; + int version = gsock->ver; + int type = gsock->type; + struct FCMSG_T fcmsgs = {0x0}; + + if ((node_idx < 0) || (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) || (stream_idx <= 0) || + (stream_idx >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) || (type == GSOCK_PRODUCER)) { + COMM_DEBUG_LOG("(r|cls stream)\tInvalid argument: node idx[%d], stream id[%d], type[%d].", + node_idx, + stream_idx, + type); + errno = ECOMMTCPARGSINVAL; + return -1; + } + + AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); + + // step 1: get the mailbox and check the state of the cmailbox + // if it is closed, delete the entry in the hash table (g_r_htab_nodeid_skey_to_stream) + // + struct c_mailbox* cmailbox = &(C_MAILBOX(node_idx, stream_idx)); + if (cmailbox->state == MAIL_CLOSED) { + return 0; + } + + LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); + + // if it was closed or reused, we need do nothing here, + // but we will do close poll and delete the entry for sure, + // there is no side effect + if (gs_check_mailbox(cmailbox->local_version, version) == false) { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + return 0; + } + + // step 2: reset the cmailbox, close poll and delete the entry in hash table (g_r_htab_nodeid_skey_to_stream) + // + COMM_DEBUG_LOG("(r|cls stream)\tTo close stream[%d] for node[%d]:%s.", + stream_idx, + node_idx, + REMOTE_NAME(g_instance.comm_cxt.g_r_node_sock, node_idx)); + + gs_r_close_logic_connection(cmailbox, ECOMMTCPAPPCLOSE, &fcmsgs); + + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + + // Send close ctrl msg to remote without cmailbox lock + (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[node_idx], &fcmsgs, ROLE_CONSUMER); + + return 0; +} // gs_r_close_stream + +// sender close logic stream, call by Producer thread when it failed to send data +int gs_s_close_stream(gsocket* gsock) +{ + int node_idx = gsock->idx; + int stream_idx = gsock->sid; + int version = gsock->ver; + int type = gsock->type; + struct FCMSG_T fcmsgs = {0x0}; + + if ((node_idx < 0) || (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) || (stream_idx <= 0) || + (stream_idx >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) || (type == GSOCK_CONSUMER)) { + COMM_DEBUG_LOG("(s|cls stream)\tInvalid argument: node idx[%d], stream id[%d], type[%d].", + node_idx, + stream_idx, + type); + errno = ECOMMTCPARGSINVAL; + return -1; + } + + AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); + + // step 1: get the mailbox and check the state of the cmailbox, + // if the keys of the pmailbox is not matched, return error + // + struct p_mailbox* pmailbox = &P_MAILBOX(node_idx, stream_idx); + LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock); + + if (gs_check_mailbox(pmailbox->local_version, version) == false) { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); + return 0; + } + + // step 2: reset the state of the pmailbox + // + COMM_DEBUG_LOG("(s|cls stream)\tTo close stream[%d] for node[%d]:%s.", + stream_idx, + node_idx, + REMOTE_NAME(g_instance.comm_cxt.g_s_node_sock, node_idx)); + + gs_s_close_logic_connection(pmailbox, ECOMMTCPAPPCLOSE, &fcmsgs); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); + // Send close ctrl msg to remote without pmailbox lock + (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[node_idx], &fcmsgs, ROLE_PRODUCER); + + return 0; +} + +// close logic socket, it will return when the sock type is invalid. +void gs_close_gsocket(gsocket* gsock) +{ + bool TempImmediateInterruptOK = t_thrd.int_cxt.ImmediateInterruptOK; + t_thrd.int_cxt.ImmediateInterruptOK = false; + + if (gsock->type == GSOCK_INVALID) { + LIBCOMM_INTERFACE_END(false, TempImmediateInterruptOK); + return; + } + + AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); + + if (gsock->type == GSOCK_DAUL_CHANNEL || gsock->type == GSOCK_PRODUCER) { + (void)gs_s_close_stream(gsock); + } + + if (gsock->type == GSOCK_DAUL_CHANNEL || gsock->type == GSOCK_CONSUMER) { + (void)gs_r_close_stream(gsock); + } + + *gsock = GS_INVALID_GSOCK; + + LIBCOMM_INTERFACE_END(false, TempImmediateInterruptOK); + return; +} + +bool gs_stop_query(gsocket* gsock, uint32 remote_pid) +{ + struct FCMSG_T fcmsgs = {0x0}; + int rc; + errno_t ss_rc; + uint32 cpylen; + + fcmsgs.type = CTRL_STOP_QUERY; + fcmsgs.node_idx = gsock->idx; + fcmsgs.streamid = gsock->sid; + fcmsgs.version = gsock->ver; + fcmsgs.query_id = DEBUG_QUERY_ID; + fcmsgs.extra_info = remote_pid; + cpylen = comm_get_cpylen(g_instance.comm_cxt.localinfo_cxt.g_self_nodename, NAMEDATALEN); + ss_rc = memset_s(fcmsgs.nodename, NAMEDATALEN, 0x0, NAMEDATALEN); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strncpy_s(fcmsgs.nodename, NAMEDATALEN, g_instance.comm_cxt.localinfo_cxt.g_self_nodename, cpylen + 1); + securec_check(ss_rc, "\0", "\0"); + fcmsgs.nodename[cpylen] = '\0'; + + rc = gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[gsock->idx], &fcmsgs, ROLE_CONSUMER); + + return (rc > 0); +} + +/* get the error information of communication layer */ +const char* gs_comm_strerror() +{ + bool savedVal = t_thrd.int_cxt.ImmediateInterruptOK; + t_thrd.int_cxt.ImmediateInterruptOK = false; + const char *errMsg = mc_strerror(errno); + t_thrd.int_cxt.ImmediateInterruptOK = savedVal; + return errMsg; +} + +/* get communication layer stream status at receiver end as a tuple for pg_comm_stream_status */ +bool get_next_recv_stream_status(CommRecvStreamStatus* stream_status) +{ + int idx, sid; + uint32 time_now = (uint32)mc_timers_ms(); + uint64 run_time = 0; + struct c_mailbox* cmailbox = NULL; + + /* if node index is invalid or stream index is invalid, return false */ + if (stream_status->idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num || + stream_status->stream_id >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) { + return false; + } + + /* traves all mailbox and get the stream status in C_MAILBOX. */ + for (idx = stream_status->idx; idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; idx++) { + for (sid = stream_status->stream_id + 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { + /* do not need return the closed stream status. */ + cmailbox = &C_MAILBOX(idx, sid); + if (cmailbox->state != MAIL_CLOSED) { + stream_status->idx = cmailbox->idx; + stream_status->stream_id = cmailbox->streamid; + stream_status->stream_state = stream_stat_string(cmailbox->state); + stream_status->quota_size = cmailbox->bufCAP; + stream_status->query_id = cmailbox->query_id; + stream_status->stream_key = cmailbox->stream_key; + stream_status->buff_usize = cmailbox->buff_q->u_size; + stream_status->bytes = cmailbox->statistic ? cmailbox->statistic->recv_bytes : 0; + stream_status->local_thread_id = cmailbox->local_thread_id; + stream_status->peer_thread_id = cmailbox->peer_thread_id; + stream_status->time = cmailbox->statistic ? (uint64)(time_now - cmailbox->statistic->start_time) : 0; + + run_time = (stream_status->time > 0) ? stream_status->time : 1; + stream_status->speed = stream_status->bytes * 1000 / run_time; + + stream_status->tcp_sock = g_instance.comm_cxt.g_r_node_sock[idx].ctrl_tcp_sock; + errno_t ss_rc; + ss_rc = strcpy_s( + stream_status->remote_host, HOST_ADDRSTRLEN, g_instance.comm_cxt.g_r_node_sock[idx].remote_host); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strcpy_s( + stream_status->remote_node, NAMEDATALEN, g_instance.comm_cxt.g_r_node_sock[idx].remote_nodename); + securec_check(ss_rc, "\0", "\0"); + + return true; + } + } + /* set stream index to -1 for next node */ + stream_status->stream_id = -1; + } + + return false; +} + +/* get communication layer stream status at sender end as a tuple for pg_comm_send_stream */ +bool get_next_send_stream_status(CommSendStreamStatus* stream_status) +{ + int idx, sid; + uint32 time_now = (uint32)mc_timers_ms(); + uint64 run_time = 0; + struct p_mailbox* pmailbox = NULL; + + /* if node index is invalid or stream index is invalid, return false */ + if (stream_status->idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num || + stream_status->stream_id >= g_instance.comm_cxt.counters_cxt.g_max_stream_num) { + return false; + } + + /* traves all mailbox and get the stream status in P_MAILBOX. */ + for (idx = stream_status->idx; idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; idx++) { + for (sid = stream_status->stream_id + 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { + /* no need return the closed stream status */ + pmailbox = &P_MAILBOX(idx, sid); + if (pmailbox->state != MAIL_CLOSED) { + stream_status->idx = pmailbox->idx; + stream_status->stream_id = pmailbox->streamid; + stream_status->stream_state = stream_stat_string(pmailbox->state); + stream_status->quota_size = pmailbox->bufCAP; + stream_status->query_id = pmailbox->query_id; + stream_status->stream_key = pmailbox->stream_key; + stream_status->bytes = pmailbox->statistic ? pmailbox->statistic->send_bytes : 0; + stream_status->wait_quota = pmailbox->statistic ? (uint64)pmailbox->statistic->wait_quota_overhead : 0; + stream_status->local_thread_id = pmailbox->local_thread_id; + stream_status->peer_thread_id = pmailbox->peer_thread_id; + stream_status->time = pmailbox->statistic ? (uint64)(time_now - pmailbox->statistic->start_time) : 0; + stream_status->tcp_sock = g_instance.comm_cxt.g_s_node_sock[idx].ctrl_tcp_sock; + + run_time = (stream_status->time > 0) ? stream_status->time : 1; + stream_status->speed = stream_status->bytes * 1000 / run_time; + + errno_t ss_rc; + ss_rc = strcpy_s( + stream_status->remote_host, HOST_ADDRSTRLEN, g_instance.comm_cxt.g_s_node_sock[idx].remote_host); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strcpy_s( + stream_status->remote_node, NAMEDATALEN, g_instance.comm_cxt.g_s_node_sock[idx].remote_nodename); + securec_check(ss_rc, "\0", "\0"); + + return true; + } + } + /* set stream index to -1 for next node */ + stream_status->stream_id = -1; + } + + return false; +} + +/* + * function name : get_next_comm_delay_info + * description : get libcomm delay info with delay_info->idx . + * arguments : _in_ delay_info->idx: the node index. + * _out_ delay_info: return delay info + * return value : + * true: return delay info. + * false: no delay info. + */ +bool get_next_comm_delay_info(CommDelayInfo* delay_info) +{ + int node_idx = delay_info->idx; + int array_idx = -1; + uint32 delay = 0; + uint32 delay_min = 0; + uint32 delay_max = 0; + uint32 delay_sum = 0; + + if (g_instance.comm_cxt.g_delay_survey_switch == false) { + g_instance.comm_cxt.g_delay_survey_start_time = mc_timers_ms(); + LIBCOMM_ELOG(LOG, "delay survey switch is open"); + } + g_instance.comm_cxt.g_delay_survey_switch = true; + + /* if node index is invalid, return false */ + if (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) { + return false; + } + + for (;;) { + if (g_instance.comm_cxt.g_senders->sender_conn[node_idx].assoc_id == 0) { + node_idx++; + } else { + break; + } + + if (node_idx >= g_instance.comm_cxt.counters_cxt.g_cur_node_num) { + return false; + } + } + + /* calculate delay info */ + for (array_idx = 0; array_idx < MAX_DELAY_ARRAY_INDEX; array_idx++) { + delay = g_instance.comm_cxt.g_delay_info[node_idx].delay[array_idx]; + if (delay < delay_min || delay_min == 0) { + delay_min = delay; + } + if (delay > delay_max) { + delay_max = delay; + } + delay_sum += delay; + } + + /* save delay info in delay_info */ + errno_t ss_rc; + ss_rc = strcpy_s(delay_info->remote_host, HOST_ADDRSTRLEN, g_instance.comm_cxt.g_s_node_sock[node_idx].remote_host); + securec_check(ss_rc, "\0", "\0"); + ss_rc = strcpy_s(delay_info->remote_node, NAMEDATALEN, g_instance.comm_cxt.g_s_node_sock[node_idx].remote_nodename); + securec_check(ss_rc, "\0", "\0"); + delay_info->stream_num = + g_instance.comm_cxt.counters_cxt.g_max_stream_num - g_instance.comm_cxt.g_usable_streamid[node_idx].count - 1; + delay_info->min_delay = delay_min; + delay_info->dev_delay = delay_sum / MAX_DELAY_ARRAY_INDEX; + delay_info->max_delay = delay_max; + + /* move to next node index */ + delay_info->idx = node_idx + 1; + + return true; +} + +/* get communication layer status as a tuple for pg_comm_status */ +bool gs_get_comm_stat(CommStat* comm_stat) +{ + if (comm_stat == NULL || g_instance.comm_cxt.counters_cxt.g_cur_node_num == 0) { + return false; + } + + if (g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate != NULL) { + comm_stat->postmaster = g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate[POSTMASTER]; + } + + if (g_instance.attr.attr_storage.comm_cn_dn_logic_conn == false && IS_PGXC_COORDINATOR) { + return true; + } + + int idx, sid, i; + int used_stream = 0; + struct c_mailbox* cmailbox = NULL; + struct p_mailbox* pmailbox = NULL; + + const int G_CUR_NODE_NUM = g_instance.comm_cxt.counters_cxt.g_cur_node_num; + int *libcomm_used_rate = g_instance.comm_cxt.localinfo_cxt.g_libcomm_used_rate; + long recv_bytes[G_CUR_NODE_NUM]; + int recv_count[G_CUR_NODE_NUM]; + int recv_count_speed = 0; + long recv_speed = 0; + + long send_speed = 0; + long send_bytes[G_CUR_NODE_NUM]; + int send_count[G_CUR_NODE_NUM]; + int send_count_speed = 0; + + errno_t rc = memset_s(recv_bytes, sizeof(recv_bytes), 0, sizeof(recv_bytes)); + securec_check(rc, "\0", "\0"); + rc = memset_s(recv_count, sizeof(recv_count), 0, sizeof(recv_count)); + securec_check(rc, "\0", "\0"); + rc = memset_s(send_bytes, sizeof(send_bytes), 0, sizeof(send_bytes)); + securec_check(rc, "\0", "\0"); + rc = memset_s(send_count, sizeof(send_count), 0, sizeof(send_count)); + securec_check(rc, "\0", "\0"); + + if (libcomm_used_rate != NULL) { + comm_stat->postmaster = libcomm_used_rate[POSTMASTER]; + comm_stat->gs_sender_flow = libcomm_used_rate[GS_SEND_flow]; + comm_stat->gs_receiver_flow = libcomm_used_rate[GS_RECV_FLOW]; + comm_stat->gs_receiver_loop = libcomm_used_rate[GS_RECV_LOOP]; + for (i = GS_RECV_LOOP + 1; i < g_instance.comm_cxt.counters_cxt.g_recv_num + GS_RECV_LOOP; i++) { + if (comm_stat->gs_receiver_loop < libcomm_used_rate[i]) { + comm_stat->gs_receiver_loop = libcomm_used_rate[i]; + } + } + } + + /* sum of recv_speed/send_speed in all stream */ + i = 0; + while (i < 2) { + /* idx: node index */ + for (idx = 0; idx < G_CUR_NODE_NUM; idx++) { + if (i == 0) { + recv_bytes[idx] = g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_bytes; + recv_count[idx] = g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_count; + send_bytes[idx] = g_instance.comm_cxt.g_senders->sender_conn[idx].comm_bytes; + send_count[idx] = g_instance.comm_cxt.g_senders->sender_conn[idx].comm_count; + /* sid: stream index */ + for (sid = 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { + cmailbox = &C_MAILBOX(idx, sid); + if (cmailbox->state != MAIL_CLOSED) { + comm_stat->buffer += cmailbox->buff_q->u_size; + } + + pmailbox = &P_MAILBOX(idx, sid); + if (pmailbox->state != MAIL_CLOSED) { + used_stream++; + } + } + } else if (i == 1) { + recv_speed += g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_bytes - recv_bytes[idx]; + recv_count_speed += g_instance.comm_cxt.g_receivers->receiver_conn[idx].comm_count - recv_count[idx]; + send_speed += g_instance.comm_cxt.g_senders->sender_conn[idx].comm_bytes - send_bytes[idx]; + send_count_speed += g_instance.comm_cxt.g_senders->sender_conn[idx].comm_count - send_count[idx]; + } + } + i++; + sleep(0.1); + } + + comm_stat->recv_speed = recv_speed * 10 / 1024; + comm_stat->recv_count_speed = recv_count_speed * 10; + comm_stat->send_speed = send_speed * 10 / 1024; + comm_stat->send_count_speed = send_count_speed * 10; + comm_stat->mem_libcomm = libcomm_used_memory; + comm_stat->mem_libpq = libpq_used_memory; + comm_stat->stream_conn_num = used_stream; + return true; +} + +/* Output the contents of structure into log file */ +void gs_log_comm_status() +{ + LIBCOMM_ELOG(LOG, "[LOG STATUS]Comm Layer Status: Do nothing now, please ignore it."); +} + +int gs_close_all_stream_by_debug_id(uint64 query_id) +{ + int idx, sid; + struct c_mailbox* cmailbox = NULL; + struct p_mailbox* pmailbox = NULL; + int cmailbox_count = 0; + int pmailbox_count = 0; + struct FCMSG_T fcmsgs = {0x0}; + + if (query_id == 0) { + LIBCOMM_ELOG(WARNING, "(cls all stream)\tInvalid argument: query id is 0!"); + return -1; + } + + AutoContextSwitch commContext(g_instance.comm_cxt.comm_global_mem_cxt); + + for (idx = 0; idx < g_instance.comm_cxt.counters_cxt.g_cur_node_num; idx++) { + for (sid = 1; sid < g_instance.comm_cxt.counters_cxt.g_max_stream_num; sid++) { + cmailbox = &C_MAILBOX(idx, sid); + if (cmailbox->query_id == query_id && cmailbox->stream_key.planNodeId != 0) { + LIBCOMM_PTHREAD_MUTEX_LOCK(&cmailbox->sinfo_lock); + + if (cmailbox->query_id == query_id && cmailbox->stream_key.planNodeId != 0 && + cmailbox->state != MAIL_CLOSED) { + cmailbox_count++; + gs_r_close_logic_connection(cmailbox, ECOMMTCPAPPCLOSE, &fcmsgs); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + /* Send close ctrl msg to remote without pmailbox lock */ + (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_r_node_sock[idx], &fcmsgs, ROLE_CONSUMER); + } else { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&cmailbox->sinfo_lock); + } + } + + pmailbox = &P_MAILBOX(idx, sid); + if (pmailbox->query_id == query_id && pmailbox->stream_key.planNodeId != 0) { + LIBCOMM_PTHREAD_MUTEX_LOCK(&pmailbox->sinfo_lock); + + if (pmailbox->query_id == query_id && pmailbox->stream_key.planNodeId != 0 && + pmailbox->state != MAIL_CLOSED) { + pmailbox_count++; + gs_s_close_logic_connection(pmailbox, ECOMMTCPAPPCLOSE, &fcmsgs); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); + /* Send close ctrl msg to remote without pmailbox lock */ + (void)gs_send_ctrl_msg(&g_instance.comm_cxt.g_s_node_sock[idx], &fcmsgs, ROLE_PRODUCER); + } else { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&pmailbox->sinfo_lock); + } + } + } + } + + if (cmailbox_count != 0 || pmailbox_count != 0) { + LIBCOMM_ELOG(LOG, + "(cls all stream)\tClose all stream by debug id[%lu], close %d cmailbox and %d pmailbox.", + query_id, + cmailbox_count, + pmailbox_count); + } + return 0; +} + +void SetupCommSignalHook() +{ + (void)gspqsignal(SIGINT, SIG_IGN); + (void)gspqsignal(SIGUSR1, SIG_IGN); + (void)gspqsignal(SIGPIPE, SIG_IGN); + + (void)gspqsignal(SIGTERM, SIG_IGN); + (void)gspqsignal(SIGQUIT, SIG_IGN); + (void)gspqsignal(SIGALRM, SIG_IGN); + (void)gspqsignal(SIGUSR2, SIG_IGN); + (void)gspqsignal(SIGFPE, SIG_IGN); + (void)gspqsignal(SIGCHLD, SIG_IGN); + /* when support guc online change, we can accept sighup, but now we don't handle it */ + (void)gspqsignal(SIGHUP, SIG_IGN); +} + +/* SIGTERM: set flag to exit normally */ +static void PoolCleanerShutdownHandler(SIGNAL_ARGS) +{ + int save_errno = errno; + + t_thrd.poolcleaner_cxt.shutdown_requested = true; + + if (t_thrd.proc) + SetLatch(&t_thrd.proc->procLatch); + + errno = save_errno; +} + +/* SIGHUP: re-read config file */ +static void PoolCleanerSighupHandler(SIGNAL_ARGS) +{ + int save_errno = errno; + + t_thrd.poolcleaner_cxt.got_SIGHUP = true; + if (t_thrd.proc) + SetLatch(&t_thrd.proc->procLatch); + + errno = save_errno; +} + +void SetupPoolerCleanSignalHook() +{ + (void)gspqsignal(SIGHUP, PoolCleanerSighupHandler); + (void)gspqsignal(SIGQUIT, SIG_IGN); + (void)gspqsignal(SIGTERM, PoolCleanerShutdownHandler); + (void)gspqsignal(SIGINT, PoolCleanerShutdownHandler); /* cancel current query */ + (void)gspqsignal(SIGALRM, SIG_IGN); /* timeout conditions */ + (void)gspqsignal(SIGPIPE, SIG_IGN); + (void)gspqsignal(SIGUSR1, SIG_IGN); + (void)gspqsignal(SIGUSR2, SIG_IGN); + (void)gspqsignal(SIGFPE, FloatExceptionHandler); + (void)gspqsignal(SIGCHLD, SIG_DFL); +} + +#ifdef ENABLE_MULTIPLE_NODES +void init_clean_pooler_idle_connections() +{ + /* we are a postmaster subprocess now */ + IsUnderPostmaster = true; + t_thrd.role = COMM_POOLER_CLEAN; + + /* reset t_thrd.proc_cxt.MyProcPid */ + t_thrd.proc_cxt.MyProcPid = gs_thread_self(); + + t_thrd.proc_cxt.MyProgName = "commPoolerCleaner"; + + /* record Start Time for logging */ + t_thrd.proc_cxt.MyStartTime = time(NULL); + + /* Identify myself via ps */ + init_ps_display("pooler cleaner process", "", "", ""); + + /* set processing mode */ + SetProcessingMode(InitProcessing); + + /* setup signal process hook */ + SetupPoolerCleanSignalHook(); + + /* We allow SIGQUIT (quickdie) at all times */ + sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); + + /* Early initialization */ + BaseInit(); + +#ifndef EXEC_BACKEND + InitProcess(); +#endif + + u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp(); + return; +} + +extern void clean_pooler_idle_connections(void); +void commPoolCleanerMain() +{ + sigjmp_buf local_sigjmp_buf; + uint64 current_time, last_start_time, poolerMaxIdleTime, nodeNameHashVal; + MemoryContext poolCleaner_context; + char *curNodeName = g_instance.attr.attr_common.PGXCNodeName; + const Size nodeNameHashMaxVal = 60000; + + ereport(LOG, (errmsg("commPoolCleanerMain started"))); + init_clean_pooler_idle_connections(); + + /* + * Create the memory context we will use in the main loop. + * + * t_thrd.mem_cxt.msg_mem_cxt is reset once per iteration of the main loop, ie, upon + * completion of processing of each command message from the client. + */ + poolCleaner_context = AllocSetContextCreate(t_thrd.top_mem_cxt, "Pool Cleaner", ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); + + (void)MemoryContextSwitchTo(poolCleaner_context); + + /* If an exception is encountered, processing resumes here. */ + int curTryCounter; + int* oldTryCounter = NULL; + + /* Normal exit */ + if (t_thrd.poolcleaner_cxt.shutdown_requested) { + g_instance.pid_cxt.CommPoolerCleanPID = 0; + proc_exit(0); /* done */ + } + + if (sigsetjmp(local_sigjmp_buf, 1) != 0) { + gstrace_tryblock_exit(true, oldTryCounter); + + /* Prevents interrupts while cleaning up */ + HOLD_INTERRUPTS(); + + /* Report the error to the server log */ + EmitErrorReport(); + + /* release resource held by lsc */ + AtEOXact_SysDBCache(false); + + (void)MemoryContextSwitchTo(poolCleaner_context); + FlushErrorState(); + + /* Flush any leaked data in the top-level context */ + MemoryContextResetAndDeleteChildren(poolCleaner_context); + + /* + * process exit. Note that because we called InitProcess, a + * callback was registered to do ProcKill, which will clean up + * necessary state. + */ + proc_exit(0); + } + oldTryCounter = gstrace_tryblock_entry(&curTryCounter); + + /* We can now handle ereport(ERROR) */ + t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; + + /* + * Unblock signals (they were blocked when the postmaster forked us) + */ + gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); + (void)gs_signal_unblock_sigusr2(); + + /* report this backend in the PgBackendStatus array */ + pgstat_report_appname("PoolCleaner"); + + /* + * Create a resource owner to keep track of our resources (currently only + * buffer pins). + */ + t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Pool cleaner", + THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_COMMUNICATION)); + SetProcessingMode(NormalProcessing); + + /* To ensure the minimum pool concept, do not clean the idle connections of each CN at the same time. + * Time difference: within 60s + */ + if (curNodeName != NULL) { + nodeNameHashVal = (uint64)(string_hash((void*)curNodeName, (strlen(curNodeName) + 1))) % nodeNameHashMaxVal; + } + + last_start_time = mc_timers_ms() + nodeNameHashVal; + for (;;) { + if (t_thrd.poolcleaner_cxt.shutdown_requested == true || g_instance.status > NoShutdown) { + break; + } + + if (t_thrd.poolcleaner_cxt.got_SIGHUP) { + t_thrd.poolcleaner_cxt.got_SIGHUP = false; + ProcessConfigFile(PGC_SIGHUP); + } + + sleep(1); + current_time = mc_timers_ms(); + poolerMaxIdleTime = (u_sess->attr.attr_network.PoolerMaxIdleTime) * MS_PER_S; + + /* If pooler_maximum_idle_time is zero, do not call clean connection procedure */ + if (poolerMaxIdleTime == 0) { + continue; + } + + if (IS_PGXC_COORDINATOR && (current_time - last_start_time) >= poolerMaxIdleTime) { + clean_pooler_idle_connections(); + last_start_time = current_time; + } + } + + /* All done, go away */ + g_instance.pid_cxt.CommPoolerCleanPID = 0; + proc_exit(0); +} +#endif + -- 2.34.1 From b24a6f074599f73b84efb8a3d60dfa0302b1f0cb Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 21:08:09 +0800 Subject: [PATCH 36/56] Delete 'src/gausskernel/cbb/communication/libcomm_common.cpp' --- .../cbb/communication/libcomm_common.cpp | 324 ------------------ 1 file changed, 324 deletions(-) delete mode 100644 src/gausskernel/cbb/communication/libcomm_common.cpp diff --git a/src/gausskernel/cbb/communication/libcomm_common.cpp b/src/gausskernel/cbb/communication/libcomm_common.cpp deleted file mode 100644 index 1743b43f2..000000000 --- a/src/gausskernel/cbb/communication/libcomm_common.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * libcomm_common.cpp - * - * IDENTIFICATION - * src/gausskernel/cbb/communication/libcomm_common.cpp - * - * ------------------------------------------------------------------------- - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libcomm_common.h" - -int binary_semaphore::init() { - atomic_set(&b_flag, 0); - atomic_set(&waiting_count, 0); - atomic_set(&b_destroy, 0); - atomic_set(&destroy_wait, 0); - int err = pthread_cond_init(&cond, NULL); - if (err != 0) - return err; - err = pthread_mutex_init(&mutex, NULL); - if (err != 0) { - LIBCOMM_PTHREAD_COND_DESTORY(&cond); - return err; - } - return err; -} - -int binary_semaphore::destroy(bool do_destroy) { - const int ret = 0; - atomic_set(&b_destroy, 1); - while (destroy_wait != 0) { - post(); - usleep(100); - } - if (do_destroy) { - LIBCOMM_PTHREAD_COND_DESTORY(&cond); - LIBCOMM_PTHREAD_MUTEX_DESTORY(&mutex); - } - return ret; -} - -void binary_semaphore::reset() { - atomic_set(&b_flag, 0); - while (destroy_wait != 0) { - post(); - usleep(100); - } -} - - void binary_semaphore::post() { - LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); - /* thread will poll up when someone has posted before */ - atomic_set(&b_flag, 1); - if (waiting_count > 0) { - LIBCOMM_PTHREAD_COND_SIGNAL(&cond); - } - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); -} - -void binary_semaphore::post_all() { - LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); - atomic_set(&b_flag, 1); - if (waiting_count > 0) { - LIBCOMM_PTHREAD_COND_BROADCAST(&cond); - } - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); -} - -int binary_semaphore::wait() { - if (b_flag) { - /* reset b_flag is no one is waitting */ - if (waiting_count == 0) - atomic_set(&b_flag, 0); - return 0; - } - - int ret = 0; - LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); - while (!b_flag) { - atomic_add(&waiting_count, 1); - ret = pthread_cond_wait(&cond, &mutex); - atomic_sub(&waiting_count, 1); - } - - if (b_destroy) - ret = -1; - /* reset b_flag is no one is waitting */ - if (waiting_count == 0) - atomic_set(&b_flag, 0); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); - - return ret; -} - -void binary_semaphore::destroy_wait_add() { - atomic_add(&destroy_wait, 1); -} - -void binary_semaphore::destroy_wait_sub() { - atomic_sub(&destroy_wait, 1); -} - -/** The parameter timeout should be in second, if it is minus or zero, the function - * is the same as _wait. - */ -int binary_semaphore::timed_wait(int timeout) { - int ret = -1; - - if (timeout <= 0) { - ret = wait(); - return ret; - } - - if (b_flag) { - /* reset b_flag is no one is waitting */ - if (waiting_count == 0) - atomic_set(&b_flag, 0); - return 0; - } - - LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); - if (b_flag) { - atomic_set(&b_flag, 0); - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); - ret = 0; - return ret; - } - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += timeout; - ts.tv_nsec = 0; - atomic_add(&waiting_count, 1); - - ret = pthread_cond_timedwait(&cond, &mutex, &ts); - - atomic_sub(&waiting_count, 1); - if (b_flag) { - /* reset b_flag is no one is waitting */ - if (waiting_count == 0) - atomic_set(&b_flag, 0); - ret = 0; - } - - if (b_destroy) - ret = -1; - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); - - return ret; -} - -int hash_entry::_init() { - return sem.init(); -} - -void hash_entry::_destroy() { - sem.destroy(true); -} - -void hash_entry::_signal() { - sem.post(); -} - -void hash_entry::_signal_all() { - sem.post_all(); -} - -void hash_entry::_wait() { - sem.wait(); -} - -void hash_entry::_hold_destroy() { - sem.destroy_wait_add(); -} - -void hash_entry::_release_destroy() { - sem.destroy_wait_sub(); -} - -int hash_entry::_timewait(int timeout) { - return sem.timed_wait(timeout); -} - -void node_sock::reset_all() { - ctrl_tcp_sock = -1; - ctrl_tcp_port = -1; - ctrl_tcp_sock_id = 0; - libcomm_reply_sock = -1; - libcomm_reply_sock_id = -1; - errno_t ss_rc = 0; - ss_rc = memset_s(remote_host, HOST_ADDRSTRLEN, 0x0, HOST_ADDRSTRLEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = memset_s(remote_nodename, NAMEDATALEN, 0x0, NAMEDATALEN); - securec_check(ss_rc, "\0", "\0"); - ss_rc = memset_s(&to_ss, sizeof(struct sockaddr_storage), 0x0, sizeof(struct sockaddr_storage)); - securec_check(ss_rc, "\0", "\0"); -} - -void node_sock::init() { - reset_all(); - LIBCOMM_PTHREAD_MUTEX_INIT(&_slock, 0); - LIBCOMM_PTHREAD_MUTEX_INIT(&_tlock, 0); -} - -void node_sock::clear() { - reset_all(); -} - -void node_sock::destroy() { - clear(); - LIBCOMM_PTHREAD_MUTEX_DESTORY(&_slock); - LIBCOMM_PTHREAD_MUTEX_DESTORY(&_tlock); -} - -void node_sock::lock() { - LIBCOMM_PTHREAD_MUTEX_LOCK(&_tlock); -} - -void node_sock::unlock() { - LIBCOMM_PTHREAD_MUTEX_UNLOCK(&_tlock); -} - -void node_sock::close_socket(int flag) { - lock(); - close_socket_nl(flag); - unlock(); -} - -void node_sock::close_socket_nl(int flag) { // close without lock - switch (flag) { - case CTRL_TCP_SOCK: - if (ctrl_tcp_sock >= 0) { - close(ctrl_tcp_sock); - ctrl_tcp_sock = -1; - ctrl_tcp_sock_id = -1; - } - break; - default: - break; - } -} - -void node_sock::set(int val, int flag) { - lock(); - set_nl(val, flag); - unlock(); -} - -void node_sock::set_nl(int val, int flag) { // set without lock - switch (flag) { - case CTRL_TCP_SOCK: - ctrl_tcp_sock = val; - break; - case CTRL_TCP_PORT: - ctrl_tcp_port = val; - break; - case CTRL_TCP_SOCK_ID: - ctrl_tcp_sock_id = val; - break; - default: - break; - } -} - -int node_sock::get(int flag, int* id) { - int val = -1; - lock(); - val = get_nl(flag, id); - unlock(); - return val; -} - -int node_sock::get_nl(int flag, int* id) const { // get without lock - int val = -1; - switch (flag) { - case CTRL_TCP_SOCK: - val = ctrl_tcp_sock; - if (id != NULL) - *id = ctrl_tcp_sock_id; - break; - case CTRL_TCP_PORT: - val = ctrl_tcp_port; - break; - case CTRL_TCP_SOCK_ID: - val = ctrl_tcp_sock_id; - if (id != NULL) - *id = ctrl_tcp_sock_id; - break; - default: - break; - } - return val; -} - -- 2.34.1 From 3dc6446442600f606dfa3da1f1fc4b50a80ff993 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 21:08:29 +0800 Subject: [PATCH 37/56] ADD file via upload --- .../cbb/communication/libcomm_common.cpp | 324 ++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 src/gausskernel/cbb/communication/libcomm_common.cpp diff --git a/src/gausskernel/cbb/communication/libcomm_common.cpp b/src/gausskernel/cbb/communication/libcomm_common.cpp new file mode 100644 index 000000000..9069de610 --- /dev/null +++ b/src/gausskernel/cbb/communication/libcomm_common.cpp @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * libcomm_common.cpp + * + * IDENTIFICATION + * src/gausskernel/cbb/communication/libcomm_common.cpp + * + * ------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libcomm_common.h" + +int binary_semaphore::init() { + atomic_set(&b_flag, 0); // 初始化 b_flag 为 0 + atomic_set(&waiting_count, 0); // 初始化 waiting_count 为 0 + atomic_set(&b_destroy, 0); // 初始化 b_destroy 为 0 + atomic_set(&destroy_wait, 0); // 初始化 destroy_wait 为 0 + int err = pthread_cond_init(&cond, NULL); // 初始化 cond,若失败则返回错误码 + if (err != 0) + return err; + err = pthread_mutex_init(&mutex, NULL); // 初始化 mutex,若失败则销毁 cond,并返回错误码 + if (err != 0) { + LIBCOMM_PTHREAD_COND_DESTORY(&cond); + return err; + } + return err; // 返回错误码 +} + +int binary_semaphore::destroy(bool do_destroy) { + const int ret = 0; + atomic_set(&b_destroy, 1); // 将 b_destroy 置为 1 表示要销毁 + while (destroy_wait != 0) { // 若存在等待销毁的线程,则发送信号,并休眠 100 微秒 + post(); + usleep(100); + } + if (do_destroy) { // 若指定要进行销毁,则销毁 cond 和 mutex + LIBCOMM_PTHREAD_COND_DESTORY(&cond); + LIBCOMM_PTHREAD_MUTEX_DESTORY(&mutex); + } + return ret; // 返回 0 +} + +void binary_semaphore::reset() { + atomic_set(&b_flag, 0); // 将 b_flag 重置为 0 + while (destroy_wait != 0) { // 若存在等待销毁的线程,则发送信号,并休眠 100 微秒 + post(); + usleep(100); + } +} + +void binary_semaphore::post() { + LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); // 上锁 mutex + /* thread will poll up when someone has posted before */ + atomic_set(&b_flag, 1); // 将 b_flag 置为 1 表示已有线程发送信号 + if (waiting_count > 0) { // 若存在等待的线程,则发送一个信号给等待线程 + LIBCOMM_PTHREAD_COND_SIGNAL(&cond); + } + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); // 解锁 mutex +} + +void binary_semaphore::post_all() { + LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); // 上锁 mutex + atomic_set(&b_flag, 1); // 将 b_flag 置为 1 表示已有线程发送信号 + if (waiting_count > 0) { // 若存在等待的线程,则发送广播给所有等待线程 + LIBCOMM_PTHREAD_COND_BROADCAST(&cond); + } + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); // 解锁 mutex +} + +int binary_semaphore::wait() { + if (b_flag) { // 若 b_flag 为 1,则表示已有线程发送信号,不需要等待 + /* reset b_flag is no one is waitting */ + if (waiting_count == 0) + atomic_set(&b_flag, 0); + return 0; + } + + int ret = 0; + LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); // 上锁 mutex + while (!b_flag) { // 若 b_flag 为 0,则表示没有线程发送信号,需要等待 + atomic_add(&waiting_count, 1); + ret = pthread_cond_wait(&cond, &mutex); // 等待信号,并当有信号到来时解锁 mutex 并接收信号 + atomic_sub(&waiting_count, 1); + } + + if (b_destroy) // 若 b_destroy 为 1,则表示要销毁,返回错误码 + ret = -1; + /* reset b_flag is no one is waitting */ + if (waiting_count == 0) + atomic_set(&b_flag, 0); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); // 解锁 mutex + + return ret; // 返回错误码 +} + +void binary_semaphore::destroy_wait_add() { + atomic_add(&destroy_wait, 1); // 销毁等待数 +1 +} + +void binary_semaphore::destroy_wait_sub() { + atomic_sub(&destroy_wait, 1); // 销毁等待数 -1 +} + +/** The parameter timeout should be in second, if it is minus or zero, the function + * is the same as _wait. + */ +int binary_semaphore::timed_wait(int timeout) { + int ret = -1; + + if (timeout <= 0) { // 若超时时间小于等于 0,则与 _wait() 函数一样 + ret = wait(); + return ret; + } + + if (b_flag) { // b_flag 为 1,表示已有线程发送信号,不需要等待 + /* reset b_flag is no one is waitting */ + if (waiting_count == 0) + atomic_set(&b_flag, 0); + return 0; + } + + LIBCOMM_PTHREAD_MUTEX_LOCK(&mutex); // 上锁 mutex + if (b_flag) { // b_flag 为 1,表示已有线程发送信号,不需要等待 + atomic_set(&b_flag, 0); + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); + ret = 0; + return ret; + } + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); // 获取当前时间 + ts.tv_sec += timeout; // 计算超时的时间点 + ts.tv_nsec = 0; + atomic_add(&waiting_count, 1); // 增加等待线程数 + + ret = pthread_cond_timedwait(&cond, &mutex, &ts); // 等待信号,若超过超时时间仍未接收到信号,则返回 ETIMEDOUT + + atomic_sub(&waiting_count, 1); // 减少等待线程数 + if (b_flag) { // b_flag 为 1,表示已有线程发送信号,不需要等待 + /* reset b_flag is no one is waitting */ + if (waiting_count == 0) + atomic_set(&b_flag, 0); + ret = 0; + } + + if (b_destroy) // 若 b_destroy 为 1,则表示要销毁,返回错误码 + ret = -1; + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&mutex); // 解锁 mutex + + return ret; // 返回错误码 +} + +int hash_entry::_init() { + return sem.init(); // 调用 binary_semaphore 对象 sem 的 init() 函数 +} + +void hash_entry::_destroy() { + sem.destroy(true); // 调用 binary_semaphore 对象 sem 的 destroy() 函数 +} + +void hash_entry::_signal() { + sem.post(); // 调用 binary_semaphore 对象 sem 的 post() 函数 +} + +void hash_entry::_signal_all() { + sem.post_all(); // 调用 binary_semaphore 对象 sem 的 post_all() 函数 +} + +void hash_entry::_wait() { + sem.wait(); // 调用 binary_semaphore 对象 sem 的 wait() 函数 +} + +void hash_entry::_hold_destroy() { + sem.destroy_wait_add(); // 调用 binary_semaphore 对象 sem 的 destroy_wait_add() 函数 +} + +void hash_entry::_release_destroy() { + sem.destroy_wait_sub(); // 调用 binary_semaphore 对象 sem 的 destroy_wait_sub() 函数 +} + +int hash_entry::_timewait(int timeout) { + return sem.timed_wait(timeout); // 调用 binary_semaphore 对象 sem 的 timed_wait() 函数 +} + +void node_sock::reset_all() { + ctrl_tcp_sock = -1; // 重置 ctrl_tcp_sock 为 -1 表示未连接 + ctrl_tcp_port = -1; // 重置 ctrl_tcp_port 为 -1 + ctrl_tcp_sock_id = 0; // 重置 ctrl_tcp_sock_id 为 0 + libcomm_reply_sock = -1; // 重置 libcomm_reply_sock 为 -1 表示未连接 + libcomm_reply_sock_id = -1; // 重置 libcomm_reply_sock_id 为 -1 + errno_t ss_rc = 0; + ss_rc = memset_s(remote_host, HOST_ADDRSTRLEN, 0x0, HOST_ADDRSTRLEN); // 清空 remote_host 数组 + securec_check(ss_rc, "\0", "\0"); + ss_rc = memset_s(remote_nodename, NAMEDATALEN, 0x0, NAMEDATALEN); // 清空 remote_nodename 数组 + securec_check(ss_rc, "\0", "\0"); + ss_rc = memset_s(&to_ss, sizeof(struct sockaddr_storage), 0x0, sizeof(struct sockaddr_storage)); // 清空 to_ss 结构体 + securec_check(ss_rc, "\0", "\0"); +} + +void node_sock::init() { + reset_all(); // 初始化相关成员变量 + LIBCOMM_PTHREAD_MUTEX_INIT(&_slock, 0); // 初始化锁 _slock + LIBCOMM_PTHREAD_MUTEX_INIT(&_tlock, 0); // 初始化锁 _tlock +} + +void node_sock::clear() { + reset_all(); // 清空相关成员变量 +} + +void node_sock::destroy() { + clear(); // 清空相关成员变量 + LIBCOMM_PTHREAD_MUTEX_DESTORY(&_slock); // 销毁锁 _slock + LIBCOMM_PTHREAD_MUTEX_DESTORY(&_tlock); // 销毁锁 _tlock +} + +void node_sock::lock() { + LIBCOMM_PTHREAD_MUTEX_LOCK(&_tlock); // 上锁 _tlock +} + +void node_sock::unlock() { + LIBCOMM_PTHREAD_MUTEX_UNLOCK(&_tlock); // 解锁 _tlock +} + +void node_sock::close_socket(int flag) { + lock(); + close_socket_nl(flag); // 关闭 socket + unlock(); +} + +void node_sock::close_socket_nl(int flag) { // 关闭 socket,但不进行上锁 + switch (flag) { + case CTRL_TCP_SOCK: + if (ctrl_tcp_sock >= 0) { + close(ctrl_tcp_sock); // 关闭 ctrl_tcp_sock + ctrl_tcp_sock = -1; + ctrl_tcp_sock_id = -1; + } + break; + default: + break; + } +} + + +void node_sock::set(int val, int flag) { + lock(); + set_nl(val, flag); + unlock(); +} + +void node_sock::set_nl(int val, int flag) { + switch (flag) { // 根据flag的不同选择对应的操作 + case CTRL_TCP_SOCK: // 如果flag是CTRL_TCP_SOCK + ctrl_tcp_sock = val; // 将val赋值给ctrl_tcp_sock + break; // 结束该case + case CTRL_TCP_PORT: // 如果flag是CTRL_TCP_PORT + ctrl_tcp_port = val; // 将val赋值给ctrl_tcp_port + break; // 结束该case + case CTRL_TCP_SOCK_ID: // 如果flag是CTRL_TCP_SOCK_ID + ctrl_tcp_sock_id = val; // 将val赋值给ctrl_tcp_sock_id + break; // 结束该case + default: // 如果flag不是上述三种情况 + break; // 不执行任何操作 + } +} + +int node_sock::get(int flag, int* id) { + int val = -1; // 初始化val为-1 + lock(); // 加锁 + val = get_nl(flag, id); // 调用get_nl函数获取val的值 + unlock(); // 解锁 + return val; // 返回val的值 +} + +int node_sock::get_nl(int flag, int* id) const { + int val = -1; // 初始化val为-1 + switch (flag) { // 根据flag的不同选择对应的操作 + case CTRL_TCP_SOCK: // 如果flag是CTRL_TCP_SOCK + val = ctrl_tcp_sock; // 将ctrl_tcp_sock的值赋给val + if (id != NULL) // 如果id不为空指针 + *id = ctrl_tcp_sock_id; // 将ctrl_tcp_sock_id的值赋给id所指向的变量 + break; // 结束该case + case CTRL_TCP_PORT: // 如果flag是CTRL_TCP_PORT + val = ctrl_tcp_port; // 将ctrl_tcp_port的值赋给val + break; // 结束该case + case CTRL_TCP_SOCK_ID: // 如果flag是CTRL_TCP_SOCK_ID + val = ctrl_tcp_sock_id; // 将ctrl_tcp_sock_id的值赋给val + if (id != NULL) // 如果id不为空指针 + *id = ctrl_tcp_sock_id; // 将ctrl_tcp_sock_id的值赋给id所指向的变量 + break; // 结束该case + default: // 如果flag不是上述三种情况 + break; // 不执行任何操作 + } + return val; // 返回val的值 +} -- 2.34.1 From c0906c50ef76e3476e990ad857d87a8a7eed49cf Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:07:05 +0800 Subject: [PATCH 38/56] Delete 'src/gausskernel/process/datasource/datasource.cpp' --- .../process/datasource/datasource.cpp | 130 ------------------ 1 file changed, 130 deletions(-) delete mode 100644 src/gausskernel/process/datasource/datasource.cpp diff --git a/src/gausskernel/process/datasource/datasource.cpp b/src/gausskernel/process/datasource/datasource.cpp deleted file mode 100644 index b26ab9d85..000000000 --- a/src/gausskernel/process/datasource/datasource.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * datasource.cpp - * support for data source - * - * IDENTIFICATION - * src/gausskernel/process/datasource/datasource.cpp - * - * ------------------------------------------------------------------------- - */ -#include "postgres.h" -#include "knl/knl_variable.h" - -#include "access/reloptions.h" -#include "catalog/pg_extension_data_source.h" -#include "datasource/datasource.h" -#include "lib/stringinfo.h" -#include "miscadmin.h" -#include "utils/builtins.h" -#include "utils/memutils.h" -#include "utils/rel.h" -#include "utils/rel_gs.h" -#include "utils/syscache.h" - -/* - * get_data_source_oid - * look up the OID by source name - * - * @IN sourcename: source name - * @IN missing_ok: If missing_ok is false, throw an error if name not found. - * If true, just return InvalidOid. - * @RETURN: oid of the data source. - */ -Oid get_data_source_oid(const char* sourcename, bool missing_ok) -{ - Oid oid; - - oid = GetSysCacheOid1(DATASOURCENAME, CStringGetDatum(sourcename)); - - if (!OidIsValid(oid) && !missing_ok) - ereport(ERROR, - (errmodule(MOD_EC), errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("source \"%s\" does not exist", sourcename))); - return oid; -} - -/* - * GetDataSource - * look up the data source definition - * - * @IN sourceid: data source oid - * @RETURN: a data source - */ -DataSource* GetDataSource(Oid sourceid) -{ - Form_pg_extension_data_source sourceform = NULL; - DataSource* source = NULL; - HeapTuple tp = NULL; - Datum datum; - bool isnull = false; - - tp = SearchSysCache1(DATASOURCEOID, ObjectIdGetDatum(sourceid)); - - if (!HeapTupleIsValid(tp)) - ereport(ERROR, - (errmodule(MOD_EC), - errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("cache lookup failed for data source %u", sourceid))); - - sourceform = (Form_pg_extension_data_source)GETSTRUCT(tp); - - source = (DataSource*)palloc0(sizeof(DataSource)); - source->sourceid = sourceid; - source->srcname = pstrdup(NameStr(sourceform->srcname)); - source->owner = sourceform->srcowner; - - /* Extract source type */ - datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srctype, &isnull); - source->srctype = isnull ? NULL : pstrdup(TextDatumGetCString(datum)); - - /* Extract source version */ - datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srcversion, &isnull); - source->srcversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum)); - - /* Extract the srcoptions */ - datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srcoptions, &isnull); - if (isnull) - source->options = NIL; - else - source->options = untransformRelOptions(datum); - - ReleaseSysCache(tp); - - return source; -} - -/* - * GetDataSourceByName - * look up the data source definition by name. - * - * @IN sourcename: source name - * @IN missing_ok: missing source name ok - * @RETURN: data source - */ -DataSource* GetDataSourceByName(const char* sourcename, bool missing_ok) -{ - Oid sourceid; - - if (sourcename == NULL) - return NULL; - - sourceid = get_data_source_oid(sourcename, missing_ok); - - if (!OidIsValid(sourceid)) - return NULL; - - return GetDataSource(sourceid); -} -- 2.34.1 From c4296f02bbaf707d6fd9b970e1778d8f33d2c882 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:07:39 +0800 Subject: [PATCH 39/56] ADD file via upload --- src/gausskernel/process/datasource.cpp | 135 +++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/gausskernel/process/datasource.cpp diff --git a/src/gausskernel/process/datasource.cpp b/src/gausskernel/process/datasource.cpp new file mode 100644 index 000000000..e1bf788aa --- /dev/null +++ b/src/gausskernel/process/datasource.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * datasource.cpp + * support for data source + * + * IDENTIFICATION + * src/gausskernel/process/datasource/datasource.cpp + * + * ------------------------------------------------------------------------- + *//* + * data_source.c + * Support functions for operations on data sources. + * + * This module is responsible for interfacing with the pg_extension_data_source + * system catalog table, and providing a low-level API for working with these + * objects. It is also responsible for interpreting some of the common fields + * that are used throughout the system. + * + */ + +#include "postgres.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "catalog/catalog.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/pg_extension_data_source.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/syscache.h" + + +/* + * get_data_source_oid - 通过数据源名称获取对应的oid + * + * @param sourcename: 数据源名称 + * @param missing_ok: 是否允许缺失,true表示允许,false表示不允许 + * @return 返回对应的oid,如果数据源不存在且不允许缺失,则抛出错误 + */ +Oid get_data_source_oid(const char* sourcename, bool missing_ok) +{ + Oid oid; + + oid = GetSysCacheOid1(DATASOURCENAME, CStringGetDatum(sourcename)); + + if (!OidIsValid(oid) && !missing_ok) + ereport(ERROR, + (errmodule(MOD_EC), errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("source \"%s\" does not exist", sourcename))); + return oid; +} + +/** + * GetDataSource - 查找数据源定义 + * + * @param sourceid: 数据源oid + * @return 返回数据源 + */ +DataSource* GetDataSource(Oid sourceid) +{ + Form_pg_extension_data_source sourceform = NULL; + DataSource* source = NULL; + HeapTuple tp = NULL; + Datum datum; + bool isnull = false; + + tp = SearchSysCache1(DATASOURCEOID, ObjectIdGetDatum(sourceid)); + + if (!HeapTupleIsValid(tp)) + ereport(ERROR, + (errmodule(MOD_EC), + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("cache lookup failed for data source %u", sourceid))); + + sourceform = (Form_pg_extension_data_source)GETSTRUCT(tp); + + source = (DataSource*)palloc0(sizeof(DataSource)); + source->sourceid = sourceid; + source->srcname = pstrdup(NameStr(sourceform->srcname)); + source->owner = sourceform->srcowner; + + /* Extract source type */ + datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srctype, &isnull); + source->srctype = isnull ? NULL : pstrdup(TextDatumGetCString(datum)); + + /* Extract source version */ + datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srcversion, &isnull); + source->srcversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum)); + + /* Extract the srcoptions */ + datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srcoptions, &isnull); + if (isnull) + source->options = NIL; + else + source->options = untransformRelOptions(datum); + + ReleaseSysCache(tp); + + return source; +} + +/** + * GetDataSourceByName - 通过名称查找数据源定义 + * + * @param sourcename: 数据源名称 + * @param missing_ok: 是否允许缺失,true表示允许,false表示不允许 + * @return 返回数据源,如果数据源不存在且允许缺失,则返回NULL + */ +DataSource* GetDataSourceByName(const char* sourcename, bool missing_ok) +{ + Oid sourceid; + + if (sourcename == NULL) + return NULL; + + sourceid = get_data_source_oid(sourcename, missing_ok); + + if (!OidIsValid(sourceid)) + return NULL; + + return GetDataSource(sourceid); +} + -- 2.34.1 From c4379e1f7e64e0f9fe643e1c61a6690a42f4876b Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:08:00 +0800 Subject: [PATCH 40/56] ADD file via upload --- .../process/datasource/datasource.cpp | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/gausskernel/process/datasource/datasource.cpp diff --git a/src/gausskernel/process/datasource/datasource.cpp b/src/gausskernel/process/datasource/datasource.cpp new file mode 100644 index 000000000..e1bf788aa --- /dev/null +++ b/src/gausskernel/process/datasource/datasource.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * datasource.cpp + * support for data source + * + * IDENTIFICATION + * src/gausskernel/process/datasource/datasource.cpp + * + * ------------------------------------------------------------------------- + *//* + * data_source.c + * Support functions for operations on data sources. + * + * This module is responsible for interfacing with the pg_extension_data_source + * system catalog table, and providing a low-level API for working with these + * objects. It is also responsible for interpreting some of the common fields + * that are used throughout the system. + * + */ + +#include "postgres.h" +#include "access/htup_details.h" +#include "access/reloptions.h" +#include "catalog/catalog.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/pg_extension_data_source.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/syscache.h" + + +/* + * get_data_source_oid - 通过数据源名称获取对应的oid + * + * @param sourcename: 数据源名称 + * @param missing_ok: 是否允许缺失,true表示允许,false表示不允许 + * @return 返回对应的oid,如果数据源不存在且不允许缺失,则抛出错误 + */ +Oid get_data_source_oid(const char* sourcename, bool missing_ok) +{ + Oid oid; + + oid = GetSysCacheOid1(DATASOURCENAME, CStringGetDatum(sourcename)); + + if (!OidIsValid(oid) && !missing_ok) + ereport(ERROR, + (errmodule(MOD_EC), errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("source \"%s\" does not exist", sourcename))); + return oid; +} + +/** + * GetDataSource - 查找数据源定义 + * + * @param sourceid: 数据源oid + * @return 返回数据源 + */ +DataSource* GetDataSource(Oid sourceid) +{ + Form_pg_extension_data_source sourceform = NULL; + DataSource* source = NULL; + HeapTuple tp = NULL; + Datum datum; + bool isnull = false; + + tp = SearchSysCache1(DATASOURCEOID, ObjectIdGetDatum(sourceid)); + + if (!HeapTupleIsValid(tp)) + ereport(ERROR, + (errmodule(MOD_EC), + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("cache lookup failed for data source %u", sourceid))); + + sourceform = (Form_pg_extension_data_source)GETSTRUCT(tp); + + source = (DataSource*)palloc0(sizeof(DataSource)); + source->sourceid = sourceid; + source->srcname = pstrdup(NameStr(sourceform->srcname)); + source->owner = sourceform->srcowner; + + /* Extract source type */ + datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srctype, &isnull); + source->srctype = isnull ? NULL : pstrdup(TextDatumGetCString(datum)); + + /* Extract source version */ + datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srcversion, &isnull); + source->srcversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum)); + + /* Extract the srcoptions */ + datum = SysCacheGetAttr(DATASOURCEOID, tp, Anum_pg_extension_data_source_srcoptions, &isnull); + if (isnull) + source->options = NIL; + else + source->options = untransformRelOptions(datum); + + ReleaseSysCache(tp); + + return source; +} + +/** + * GetDataSourceByName - 通过名称查找数据源定义 + * + * @param sourcename: 数据源名称 + * @param missing_ok: 是否允许缺失,true表示允许,false表示不允许 + * @return 返回数据源,如果数据源不存在且允许缺失,则返回NULL + */ +DataSource* GetDataSourceByName(const char* sourcename, bool missing_ok) +{ + Oid sourceid; + + if (sourcename == NULL) + return NULL; + + sourceid = get_data_source_oid(sourcename, missing_ok); + + if (!OidIsValid(sourceid)) + return NULL; + + return GetDataSource(sourceid); +} + -- 2.34.1 From 7d0c403139626af87a0edbdff5dfad3979c76187 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:09:34 +0800 Subject: [PATCH 41/56] Delete 'src/gausskernel/process/globalplancache/globalplancache.cpp' --- .../globalplancache/globalplancache.cpp | 1136 ----------------- 1 file changed, 1136 deletions(-) delete mode 100644 src/gausskernel/process/globalplancache/globalplancache.cpp diff --git a/src/gausskernel/process/globalplancache/globalplancache.cpp b/src/gausskernel/process/globalplancache/globalplancache.cpp deleted file mode 100644 index 96f6ecf75..000000000 --- a/src/gausskernel/process/globalplancache/globalplancache.cpp +++ /dev/null @@ -1,1136 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * globalplancache.cpp - * global plan cache - * - * IDENTIFICATION - * src/gausskernel/process/globalplancache/globalplancache.cpp - * - * ------------------------------------------------------------------------- - */ - - -#include "postgres.h" -#include "knl/knl_variable.h" - -#include "access/hash.h" -#include "access/xact.h" -#include "catalog/pgxc_node.h" -#include "commands/prepare.h" -#include "executor/lightProxy.h" -#include "executor/spi_priv.h" -#include "optimizer/nodegroups.h" -#include "opfusion/opfusion.h" -#include "pgxc/groupmgr.h" -#include "pgxc/pgxcnode.h" -#include "utils/dynahash.h" -#include "utils/globalplancache.h" -#include "utils/memutils.h" -#include "utils/plancache.h" -#include "utils/syscache.h" -#include "utils/plpgsql.h" -#include "nodes/pg_list.h" -#include "commands/sqladvisor.h" - -template void GlobalPlanCache::RemovePlanSource(CachedPlanSource* plansource, const char* stmt_name); - -template void GlobalPlanCache::RemovePlanSource(CachedPlanSource* plansource, const char* stmt_name); - -template void GlobalPlanCache::RemovePlanSource(CachedPlanSource* plansource, const char* stmt_name); - -static bool has_diff_schema(const List *list1, const List *list2) -{ - const ListCell *cell = NULL; - - if (list2 == NIL) { - return list1 != NULL; - } - foreach (cell, list1) { - if (!list_member_oid(list2, lfirst_oid(cell))) { - return true; - } - } - return false; -} - -static bool -CompareSearchPath(struct OverrideSearchPath* path1, struct OverrideSearchPath* path2) -{ - Assert(path1 != NULL); - - if (path2 == NULL) { - return OverrideSearchPathMatchesCurrent(path1); - } - - if (path1 == path2) { - return true; - } - - if (path1->addTemp != path2->addTemp) { - return false; - } - if (path1->addCatalog != path2->addCatalog) { - return false; - } - if (has_diff_schema(path1->schemas, path2->schemas)) { - return false; - } - if (has_diff_schema(path2->schemas, path1->schemas)) { - return false; - } - return true; -} - -static bool GPCCompareParam(Oid* params1, Oid* params2, int paramNum) -{ - for (int i = 0; i < paramNum; i++) { - if (params1[i] != params2[i]) { - return false; - } - } - return true; -} - -/* - * Return false when the given compilation environment matches the current - * session compilation environment, mainly compares GUC parameter settings. - */ -static bool -GPCCompareEnv(GPCEnv *env1, GPCEnv *env2) -{ - Assert (env1 != NULL); - Assert (env2 != NULL); - if (memcmp(&env1->plainenv, &env2->plainenv, sizeof(GPCPlainEnv)) == 0 - && strncmp(env1->default_storage_nodegroup, env2->default_storage_nodegroup, NAMEDATALEN) == 0 - && strncmp(env1->expected_computing_nodegroup, env2->expected_computing_nodegroup, NAMEDATALEN) == 0 - && env1->num_params == env2->num_params) - { - if (!GPCCompareParam(env1->param_types, env2->param_types, env1->num_params)) { - return false; - } - if (CompareSearchPath(env1->search_path, env2->search_path)) { - if (env1->depends_on_role != env2->depends_on_role && env1->user_oid != env2->user_oid) { - return false; - } else if (env1->depends_on_role && env2->depends_on_role && env1->user_oid != env2->user_oid) { - return false; - } else { - return true; - } - } - } - - return false; -} - -uint32 GPCHashFunc(const void *key, Size keysize) -{ - const GPCKey *item = (const GPCKey *) key; - uint32 val1 = DatumGetUInt32(hash_any((const unsigned char *)item->query_string, item->query_length)); - uint32 val2 = DatumGetUInt32(hash_any((const unsigned char *)(&item->env.plainenv), sizeof(GPCPlainEnv))); - uint32 val3 = DatumGetUInt32(hash_any((const unsigned char *)(&item->spi_signature), sizeof(SPISign))); - val1 ^= val2; - val1 ^= val3; - - return val1; -} - -int GPCKeyMatch(const void *left, const void *right, Size keysize) -{ - GPCKey *leftItem = (GPCKey*)left; - GPCKey *rightItem = (GPCKey*)right; - Assert(NULL != leftItem); - Assert(NULL != rightItem); - - /* we just care whether the result is 0 or not. */ - if (leftItem->query_length != rightItem->query_length) { - return 1; - } - - if(strncmp(leftItem->query_string, rightItem->query_string, leftItem->query_length)) { - return 1; - } - - if(GPCCompareEnv(&(leftItem->env), &(rightItem->env)) == false) { - return 1; - } - - return 0; -} - -void GPCKeyDeepCopy(const GPCKey *srcGpckey, GPCKey *destGpckey) -{ - *destGpckey = *srcGpckey; - if (srcGpckey->query_string) - destGpckey->query_string = pstrdup(srcGpckey->query_string); - - if (destGpckey->env.num_params > 0) { - destGpckey->env.param_types = (Oid*)palloc(sizeof(Oid) * destGpckey->env.num_params); - errno_t rc = 0; - rc = memcpy_s(destGpckey->env.param_types, sizeof(Oid) * destGpckey->env.num_params, - srcGpckey->env.param_types, sizeof(Oid) * destGpckey->env.num_params); - securec_check(rc, "", ""); - } - - if (destGpckey->env.schema_name) { - destGpckey->env.search_path = (struct OverrideSearchPath *)palloc(sizeof(struct OverrideSearchPath)); - *destGpckey->env.search_path = *srcGpckey->env.search_path; - destGpckey->env.search_path->schemas = list_copy(srcGpckey->env.search_path->schemas); - } -} - -/***************** - global plan cache - *****************/ - -GlobalPlanCache::GlobalPlanCache() -{ - Init(); -} - -GlobalPlanCache::~GlobalPlanCache() -{ -} - -void GlobalPlanCache::Init() -{ - HASHCTL ctl; - errno_t rc = 0; - rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl)); - securec_check(rc, "\0", "\0"); - ctl.keysize = sizeof(GPCKey); - ctl.entrysize = sizeof(GPCEntry); - ctl.hash = (HashValueFunc)GPCHashFunc; - ctl.match = (HashCompareFunc)GPCKeyMatch; - - int flags = HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_COMPARE | HASH_EXTERN_CONTEXT | HASH_NOEXCEPT; - - m_array = (GPCHashCtl *) MemoryContextAllocZero(GLOBAL_PLANCACHE_MEMCONTEXT, - sizeof(GPCHashCtl) * GPC_NUM_OF_BUCKETS); - - for (uint32 i = 0; i < GPC_NUM_OF_BUCKETS; i++) { - m_array[i].count = 0; - m_array[i].lockId = FirstGPCMappingLock + i; - - /* - * Create a MemoryContext per hash bucket so that all entries, plans etc under the bucket will live - * under this Memory context. This is for performance purposes. We do not want everything to be under - * the shared GlobalPlanCacheContext because more threads would need to synchronize everytime it needs a chunk - * of memory and that would become a bottleneck. - */ - m_array[i].context = AllocSetContextCreate(GLOBAL_PLANCACHE_MEMCONTEXT, - "GPC_Plan_Bucket_Context", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE, - SHARED_CONTEXT); - - - ctl.hcxt = m_array[i].context; - m_array[i].hash_tbl = hash_create("Global_Plan_Cache", - GPC_HTAB_SIZE, - &ctl, - flags); - - } - - m_invalid_list = NULL; -} - -bool GlobalPlanCache::TryStore(CachedPlanSource *plansource, PreparedStatement *ps) -{ - Assert (plansource != NULL); - Assert (plansource->magic == CACHEDPLANSOURCE_MAGIC); - Assert (!plansource->gpc.status.InShareTable()); - Assert (plansource->gpc.status.IsSharePlan()); - Assert (plansource->is_support_gplan || (plansource->gplan == NULL && plansource->cplan == NULL)); - - GPCKey* key = plansource->gpc.key; - - uint32 hashCode = GPCHashFunc((const void *) key, sizeof(*key)); - - uint32 bucket_id = GetBucket(hashCode); - Assert (bucket_id >= 0 && bucket_id < GPC_NUM_OF_BUCKETS); - int lock_id = m_array[bucket_id].lockId; - - (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); - MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); - - bool found = false; - GPCEntry *entry = (GPCEntry *)hash_search_with_hash_value(m_array[bucket_id].hash_tbl, - (const void*)key, hashCode, HASH_ENTER, &found); - if (entry == NULL) { - MemoryContextSwitchTo(oldcontext); - LWLockRelease(GetMainLWLockByIndex(lock_id)); - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_PSTATEMENT), - errmsg("store global plan source failed due to memory allocation failed"))); - return false; - } - - if (found == false) { - START_CRIT_SECTION(); - /* Deep copy the query_string to the GPC entry's query_string */ - entry->key.query_string = key->query_string; - entry->key.query_length = key->query_length; - /* Set the magic number. */ - entry->val.plansource = plansource; - entry->val.used_count = 0; - INSTR_TIME_SET_CURRENT(entry->val.last_use_time); - /* off the link */ - plansource->next_saved = NULL; - plansource->is_checked_opfusion = true; - if (plansource->opFusionObj != NULL) { - OpFusion::SaveInGPC((OpFusion*)(plansource->opFusionObj)); - } - /* initialize the ref count .*/ -#ifdef ENABLE_MULTIPLE_NODES - /* dn only count reference on cur_stmt_psrc, no prepare statement. - cn count reference on prepare statement */ - if (IS_PGXC_COORDINATOR) - plansource->gpc.status.AddRefcount(); -#else - plansource->gpc.status.AddRefcount(); -#endif - m_array[bucket_id].count++; - Assert(plansource->context->is_shared); - MemoryContextSeal(plansource->context); - Assert(plansource->query_context->is_shared); - MemoryContextSeal(plansource->query_context); - if (plansource->gplan) { - pg_atomic_fetch_add_u32((volatile uint32*)&plansource->gplan->global_refcount, 1); - plansource->gplan->is_share = true; - Assert(plansource->gplan->context->is_shared); - MemoryContextSeal(plansource->gplan->context); - } -#ifdef USE_ASSERT_CHECKING - else { - Assert(IS_PGXC_COORDINATOR && plansource->single_exec_node && - plansource->gplan == NULL && plansource->cplan == NULL); - } -#endif - plansource->gpc.status.SetLoc(GPC_SHARE_IN_SHARE_TABLE); - - END_CRIT_SECTION(); - - } else { - /* some guys win. */ - if (ps == NULL) { - Assert (IS_PGXC_DATANODE); - GPC_LOG("drop cache plan in try store", plansource, 0); - DropCachedPlan(plansource); - } else { - CachedPlanSource* newsource = entry->val.plansource; - ps->plansource = newsource; - newsource->gpc.status.AddRefcount(); - INSTR_TIME_SET_CURRENT(entry->val.last_use_time); - u_sess->pcache_cxt.gpc_in_try_store = true; - - /* purge old one. */ - GPC_LOG("drop cache plan in try store", plansource, 0); -#ifdef ENABLE_MULTIPLE_NODES - if (IS_PGXC_COORDINATOR) { - GPCDropLPIfNecessary(ps->stmt_name, false, false, newsource); - } -#endif - DropCachedPlan(plansource); - u_sess->pcache_cxt.gpc_in_try_store = false; - } - } - - MemoryContextSwitchTo(oldcontext); - LWLockRelease(GetMainLWLockByIndex(lock_id)); - return true; -} - -CachedPlanSource* GlobalPlanCache::Fetch(const char *query_string, uint32 query_len, - int num_params, Oid* paramTypes, SPISign* spi_sign_ptr) -{ - GPCKey key; - key.env.filled = false; - key.query_string = query_string; - key.query_length = query_len; - EnvFill(&key.env, false); - key.env.search_path = NULL; - key.env.num_params = num_params; - key.env.param_types = paramTypes; - if (spi_sign_ptr != NULL) - key.spi_signature = *spi_sign_ptr; - else - key.spi_signature = {(uint32)-1, 0, (uint32)-1, -1}; - - uint32 hashCode = GPCHashFunc((const void *) &key, sizeof(key)); - - uint32 bucket_id = GetBucket(hashCode); - Assert (bucket_id >= 0 && bucket_id < GPC_NUM_OF_BUCKETS); - int lock_id = m_array[bucket_id].lockId; - - (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_SHARED); - MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); - - bool foundCachedEntry = false; - GPCEntry *entry = (GPCEntry *) hash_search_with_hash_value(m_array[bucket_id].hash_tbl, - (const void*)(&key), - hashCode, - HASH_FIND, - &foundCachedEntry); - - if (!foundCachedEntry) { - MemoryContextSwitchTo(oldcontext); - LWLockRelease(GetMainLWLockByIndex(lock_id)); - return NULL; - } else { - CachedPlanSource* psrc = entry->val.plansource; - psrc->gpc.status.AddRefcount(); - if (!psrc->gpc.status.IsValid()) { - MemoryContextSwitchTo(oldcontext); - LWLockRelease(GetMainLWLockByIndex(lock_id)); - MoveIntoInvalidPlanList(psrc); - psrc->gpc.status.SubRefCount(); - return NULL; - } - if (ENABLE_DN_GPC) - u_sess->pcache_cxt.private_refcount++; - pg_atomic_fetch_add_u32(&entry->val.used_count, 1); - MemoryContextSwitchTo(oldcontext); - LWLockRelease(GetMainLWLockByIndex(lock_id)); - return psrc; - } - - return NULL; -} - -void GlobalPlanCache::AddInvalidList(CachedPlanSource* plansource) -{ - (void)LWLockAcquire(GPCClearLock, LW_EXCLUSIVE); - MemoryContext oldcontext = MemoryContextSwitchTo(GLOBAL_PLANCACHE_MEMCONTEXT); - START_CRIT_SECTION(); - plansource->gpc.status.SetLoc(GPC_SHARE_IN_SHARE_TABLE_INVALID_LIST); - m_invalid_list = dlappend(m_invalid_list, plansource); - plansource->gpc.status.SetStatus(GPC_INVALID); - END_CRIT_SECTION(); - MemoryContextSwitchTo(oldcontext); - LWLockRelease(GPCClearLock); -} - -void GlobalPlanCache::DropInvalid() -{ - (void)LWLockAcquire(GPCClearLock, LW_EXCLUSIVE); - if (m_invalid_list != NULL) { - DListCell *cell = m_invalid_list->head; - while (cell != NULL) { - CachedPlanSource *curr = (CachedPlanSource *)cell->data.ptr_value; - if (curr->gpc.status.RefCountZero()) { - Assert(curr->next_saved == NULL); - DListCell *next = cell->next; - GPC_LOG("drop invalid shared plancache", curr, curr->stmt_name); - m_invalid_list = dlist_delete_cell(m_invalid_list, cell, false); - DropCachedPlanInternal(curr); - curr->magic = 0; - MemoryContextUnSeal(curr->context); - MemoryContextUnSeal(curr->query_context); - if (curr->opFusionObj) { - OpFusion::DropGlobalOpfusion((OpFusion*)(curr->opFusionObj)); - } - MemoryContextDelete(curr->context); - - cell = next; - } else { - cell = cell->next; - } - } - } - LWLockRelease(GPCClearLock); -} - -template -void GlobalPlanCache::RemovePlanSource(CachedPlanSource* plansource, const char* stmt_name) -{ - Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); - - if(plansource->gpc.status.InShareTable()) { - GPCKey* key = plansource->gpc.key; - uint32 hashCode = GPCHashFunc((const void *) key, sizeof(*key)); - uint32 bucket_id = GetBucket(hashCode); - int lock_id = m_array[bucket_id].lockId; - (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); - if (plansource->gpc.status.InShareTableInvalidList() == false) { - bool found = false; - hash_search(m_array[bucket_id].hash_tbl, (void *)key, HASH_REMOVE, &found); - if (unlikely(found == false)) - elog(PANIC, "should found plan in gpc when RemovePlanSource"); - m_array[bucket_id].count--; - AddInvalidList(plansource); - } - /* Has hold refcount for ACTION_RECREATE */ - if (action_type == ACTION_RECREATE) - plansource->gpc.status.SubRefCount(); - DropInvalid(); - LWLockRelease(GetMainLWLockByIndex(lock_id)); - - if (ENABLE_DN_GPC) { - u_sess->pcache_cxt.private_refcount--; - if (u_sess->pcache_cxt.private_refcount != 0) - elog(PANIC, "wrong refcount in subrefcount"); - } - - if (action_type == ACTION_RELOAD) { - /* clear Datanode statements */ -#ifdef ENABLE_MULTIPLE_NODES - if (plansource->gplan != NULL) - GPCCleanDatanodeStatement(plansource->gplan->dn_stmt_num, stmt_name); - else - GPCDropLPIfNecessary(stmt_name, true, true, NULL); -#endif - } - - } else { - if (action_type == ACTION_RECREATE) { - GPC_LOG("remove private plansource", plansource, plansource->stmt_name); - DropCachedPlan(plansource); - } else { - CN_GPC_LOG("invalid plan", plansource, stmt_name); - plansource->gpc.status.SetStatus(GPC_INVALID); - plansource->is_valid = false; - if (plansource->gplan) { - plansource->gplan->is_valid = false; - Assert(!plansource->gplan->isShared()); - } - if (action_type == ACTION_RELOAD) { - DropCachedPlanInternal(plansource); - if (plansource->gplan == NULL) - GPCDropLPIfNecessary(stmt_name, true, true, NULL); - } - } - } -} - -void GlobalPlanCache::RemoveEntry(uint32 htblIdx, GPCEntry *entry) -{ - CachedPlanSource *plansource = entry->val.plansource; - - bool found = false; - hash_search(m_array[htblIdx].hash_tbl, (void *) &(entry->key), HASH_REMOVE, &found); - Assert(found == true); - m_array[htblIdx].count--; - AddInvalidList(plansource); - DropInvalid(); -} - - -bool GlobalPlanCache::CheckRecreateCachePlan(CachedPlanSource* psrc, bool* hasGetLock) -{ - /* - * Start up a transaction command so we can run parse analysis etc. (Note - * that this will normally change current memory context.) Nothing happens - * if we are already in one. - */ - start_xact_command(); - Assert(psrc->magic == CACHEDPLANSOURCE_MAGIC); - /* get lock before check plan is valid or not, release it if need recreate plan */ - if (psrc->gpc.status.InShareTable()) { - AcquirePlannerLocks(psrc->query_list, true); - if (psrc->gplan) { - AcquireExecutorLocks(psrc->gplan->stmt_list, true); - } - *hasGetLock = true; - } - -#ifdef ENABLE_MULTIPLE_NODES - if (IS_PGXC_COORDINATOR && !psrc->gpc.status.InShareTable()) { - return false; - } -#else - if (!psrc->gpc.status.InShareTable()) { - return false; - } -#endif - - if (u_sess->pcache_cxt.gpc_in_ddl == true) { - return true; - } - if (!psrc->gpc.status.IsValid()) { - return true; - } - if (psrc->dependsOnRole && (psrc->rewriteRoleId != GetUserId())) { - return true; - } - if ((psrc->gplan != NULL && TransactionIdIsValid(psrc->gplan->saved_xmin))) { - return true; - } - - if (psrc->search_path && !OverrideSearchPathMatchesCurrent(psrc->search_path)) { - return true; - } - - return false; -} - -bool GlobalPlanCache::CheckRecreateSPICachePlan(SPIPlanPtr spi_plan) -{ - ListCell* cell = NULL; - Assert(spi_plan->magic == _SPI_PLAN_MAGIC); - foreach(cell, spi_plan->plancache_list) { - CachedPlanSource* plansource = (CachedPlanSource*)lfirst(cell); - bool hasGetLock = false; - if (CheckRecreateCachePlan(plansource, &hasGetLock)) { - if (hasGetLock) { - AcquirePlannerLocks(plansource->query_list, false); - if (plansource->gplan) { - AcquireExecutorLocks(plansource->gplan->stmt_list, false); - } - } - return true; - } - } - return false; -} - -void GlobalPlanCache::RecreateSPICachePlan(SPIPlanPtr spiplan) -{ - ListCell* cell = NULL; - Assert(spiplan->magic == _SPI_PLAN_MAGIC); - /* push error context stack */ - ErrorContextCallback spi_err_context; - spi_err_context.callback = _SPI_error_callback; - spi_err_context.arg = NULL; /* we'll fill this below */ - spi_err_context.previous = t_thrd.log_cxt.error_context_stack; - t_thrd.log_cxt.error_context_stack = &spi_err_context; - foreach(cell, spiplan->plancache_list) { - CachedPlanSource* oldsource = (CachedPlanSource*)lfirst(cell); - if (!oldsource->gpc.status.InShareTable()) - continue; - GPC_LOG("recreate spi cachedplan", oldsource, 0); - RecreateCachePlan(oldsource, NULL, NULL, spiplan, cell, false); - } - /* pop error context stack */ - t_thrd.log_cxt.error_context_stack = spi_err_context.previous; - Assert(SPIPlanCacheTableLookup(u_sess->SPI_cxt._current->spi_hash_key)); -} - -void GlobalPlanCache::MoveIntoInvalidPlanList(CachedPlanSource* psrc) -{ - if (psrc->gpc.status.InShareTable() && !psrc->gpc.status.IsValid()) { - GPCKey* key = psrc->gpc.key; - uint32 hashCode = GPCHashFunc((const void *) key, sizeof(*key)); - uint32 bucket_id = GetBucket(hashCode); - int lock_id = m_array[bucket_id].lockId; - (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); - if (psrc->gpc.status.InShareTableInvalidList() == false) { - bool found = false; - hash_search(m_array[bucket_id].hash_tbl, (void *)key, HASH_REMOVE, &found); - if (unlikely(found == false)) - elog(PANIC, "should found plan in gpc"); - m_array[bucket_id].count--; - AddInvalidList(psrc); - } - LWLockRelease(GetMainLWLockByIndex(lock_id)); - } -} - -void GlobalPlanCache::RecreateCachePlan(CachedPlanSource* oldsource, const char* stmt_name, PreparedStatement *entry, - SPIPlanPtr spiplan, ListCell* spiplanCell, bool hasGetLock) -{ - GPC_LOG("recreate plan", oldsource, oldsource->stmt_name); - /* these operator may throw error, make sure shared plan is invalid first */ - CachedPlanSource *newsource = NULL; - PG_TRY(); - { - if (hasGetLock) { - AcquirePlannerLocks(oldsource->query_list, false); - if (oldsource->gplan) { - AcquireExecutorLocks(oldsource->gplan->stmt_list, false); - } - } - newsource = CopyCachedPlan(oldsource, true); - MemoryContext oldcxt = MemoryContextSwitchTo(newsource->context); - newsource->stream_enabled = IsStreamSupport(); - u_sess->exec_cxt.CurrentOpFusionObj = NULL; - Assert (oldsource->gpc.status.IsSharePlan()); - newsource->gpc.status.ShareInit(); - // If the planSource is set to invalid, the AST must be analyzed again - // because the meta has changed. - newsource->is_valid = false; - bool has_lp = false; - - if (spiplan != NULL) { - t_thrd.log_cxt.error_context_stack->arg = (void *)newsource->query_string; - newsource->spi_signature = oldsource->spi_signature; - newsource->parserSetup = spiplan->parserSetup; - newsource->parserSetupArg = spiplan->parserSetupArg; - } else if (IS_PGXC_DATANODE) { - newsource->stmt_name = pstrdup(stmt_name); - } else { - newsource->stmt_name = pstrdup(stmt_name); -#ifdef ENABLE_MULTIPLE_NODES - has_lp = (oldsource->single_exec_node != NULL && oldsource->gplan == NULL && oldsource->cplan == NULL); - /* clean session's datanode statment on cn */ - if (has_lp) { - /* no lp in newsource, delete old lp */ - GPCDropLPIfNecessary(stmt_name, false, true, NULL); - } else if (oldsource->gplan != NULL) { - /* Close any active planned Datanode statements, recreate in BuildCachedPlan later */ - GPCCleanDatanodeStatement(oldsource->gplan->dn_stmt_num, stmt_name); - } -#endif - } - (void)RevalidateCachedQuery(newsource, has_lp); - MemoryContextSwitchTo(oldcxt); - } - PG_CATCH(); - { - /* catch only move invalid plansource into gpc invalid list when error occurs */ - MoveIntoInvalidPlanList(oldsource); - PG_RE_THROW(); - } - PG_END_TRY(); - - /* newsource has reference on session, forget resource owner */ - ResourceOwnerForgetGMemContext(t_thrd.utils_cxt.TopTransactionResourceOwner, newsource->context); - newsource->next_saved = u_sess->pcache_cxt.first_saved_plan; - u_sess->pcache_cxt.first_saved_plan = newsource; - newsource->is_saved = true; - newsource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_SAVE_PLAN_LIST); - if (spiplan != NULL) - spiplanCell->data.ptr_value = newsource; - else { -#ifdef ENABLE_MULTIPLE_NODES - if (IS_PGXC_DATANODE) - u_sess->pcache_cxt.cur_stmt_psrc = newsource; - else - entry->plansource = newsource; -#else - entry->plansource = newsource; -#endif - } - - RemovePlanSource(oldsource, stmt_name); -} - -void GlobalPlanCache::Commit() -{ -#ifdef ENABLE_MULTIPLE_NODES - if (IS_PGXC_COORDINATOR) { - CNCommit(); - } else { - DNCommit(); - } -#else - CNCommit(); -#endif -} - -/* - * @Description: Store the global plancache into the hash table and mark them as shared. - * this function called when a transction commited. All unsaved global plancaches are stored - * in the list named first_saved_plan. When we found that a plancache was saved by others, we - * just drop it and update the prepare pointer to the shared glboal plancache. - * @in num: void - * @return - void - */ -void GlobalPlanCache::DNCommit() -{ - CachedPlanSource *next_plansource = NULL; - CachedPlanSource *plansource = u_sess->pcache_cxt.first_saved_plan; - u_sess->pcache_cxt.first_saved_plan = NULL; - CleanSessGPCPtr(u_sess); - if (u_sess->pcache_cxt.private_refcount != 0) { - elog(PANIC, "wrong refcount"); - } - while (plansource != NULL) { - Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); - Assert(!plansource->gpc.status.InShareTable()); - if (unlikely(plansource->magic != CACHEDPLANSOURCE_MAGIC)) { - ereport(PANIC, - (errcode(ERRCODE_UNDEFINED_PSTATEMENT), - errmsg("In gpc commit stage, plansource has already been freed"))); - } - - next_plansource = plansource->next_saved; - if (plansource->gpc.status.IsPrivatePlan()) { - /* private plan has reference on pointer like unname_stmt_psrc or spiplan */ - GPC_LOG("invalid plan in commit", plansource, plansource->stmt_name); - plansource->is_valid = false; - plansource->next_saved = NULL; - plansource->gpc.status.SetLoc(GPC_SHARE_IN_PREPARE_STATEMENT); - plansource->gpc.status.SetStatus(GPC_INVALID); - } else if (!plansource->is_valid || plansource->gplan == NULL || !plansource->is_support_gplan) { - GPC_LOG("drop plan in commit", plansource, plansource->stmt_name); - /* no prepare statement on dn, so we just drop shared plansource if can't save it in gpc, in case leak */ - DropCachedPlan(plansource); - } else { - TryStore(plansource, NULL); - } - plansource = next_plansource; - } -} - -void GlobalPlanCache::CNCommit() -{ - CachedPlanSource *next_plansource = NULL; - CachedPlanSource *plansource = u_sess->pcache_cxt.first_saved_plan; - u_sess->pcache_cxt.first_saved_plan = NULL; - while (plansource != NULL) { - Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); - Assert(!plansource->gpc.status.InShareTable()); - if (unlikely(plansource->magic != CACHEDPLANSOURCE_MAGIC)) { - ereport(PANIC, - (errcode(ERRCODE_UNDEFINED_PSTATEMENT), - errmsg("In gpc commit stage, plansource has already been freed"))); - } - - next_plansource = plansource->next_saved; - bool has_lp = false; -#ifdef ENABLE_MULTIPLE_NODES - has_lp = plansource->single_exec_node && - plansource->gplan == NULL && plansource->cplan == NULL && plansource->stmt_name; - if (has_lp) { - has_lp = (lightProxy::locateLpByStmtName(plansource->stmt_name) != NULL); - } -#endif - if (!plansource->gpc.status.IsSharePlan() || (plansource->gplan == NULL && plansource->cplan)) { - /* stream or private plan or cplan need put into ungpc_save_plan */ - plansource->is_saved = true; - if (!plansource->is_support_gplan && plansource->gpc.status.IsSharePlan()) - plansource->gpc.status.SetKind(GPC_CPLAN); - Assert (!plansource->gpc.status.IsSharePlan()); - plansource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_UNGPC_PLAN_LIST); - plansource->next_saved = u_sess->pcache_cxt.ungpc_saved_plan; - u_sess->pcache_cxt.ungpc_saved_plan = plansource; - } else if (!plansource->is_valid || (plansource->gplan && !plansource->gplan->is_valid)) { - plansource->is_valid = false; - plansource->is_saved = true; - Assert (plansource->gpc.status.IsSharePlan()); - plansource->gpc.status.SetStatus(GPC_INVALID); - plansource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_SAVE_PLAN_LIST); - plansource->next_saved = u_sess->pcache_cxt.first_saved_plan; - u_sess->pcache_cxt.first_saved_plan = plansource; - } else if (plansource->gplan == NULL && plansource->cplan == NULL && !has_lp) { - /* get commit before create cachedplan or lp, not init gpckey. keep in first_saved_plan */ - plansource->is_saved = true; - Assert (plansource->gpc.status.IsSharePlan()); - plansource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_SAVE_PLAN_LIST); - plansource->next_saved = u_sess->pcache_cxt.first_saved_plan; - u_sess->pcache_cxt.first_saved_plan = plansource; - } else { - if (plansource->spi_signature.spi_key != INVALID_SPI_KEY) { - Assert(!has_lp); - Assert(plansource->gplan); - Assert(plansource->is_support_gplan); - g_instance.plan_cache->SPICommit(plansource); - } else { - PreparedStatement* ps = FetchPreparedStatement(plansource->stmt_name, true, false); - if (unlikely(ps == NULL)) { -#ifdef MEMORY_CONTEXT_CHECKING - ereport(PANIC, -#else - ereport(ERROR, -#endif - (errcode(ERRCODE_UNDEFINED_PSTATEMENT), - errmsg("In gpc commit stage, fail to fetch prepare statement:%s", plansource->stmt_name))); - } - TryStore(plansource, ps); - } - } - - plansource = next_plansource; - } -} - -void GlobalPlanCache::SPITryStore(CachedPlanSource* plansource, SPIPlanPtr spiplan, int nth) -{ - Assert (plansource != NULL); - Assert (plansource->magic == CACHEDPLANSOURCE_MAGIC); - Assert (!plansource->gpc.status.InShareTable()); - Assert (plansource->gpc.status.IsSharePlan()); - Assert (spiplan->saved); - - GPCKey* key = plansource->gpc.key; - - uint32 hashCode = GPCHashFunc((const void *) key, sizeof(*key)); - - uint32 bucket_id = GetBucket(hashCode); - Assert (bucket_id >= 0 && bucket_id < GPC_NUM_OF_BUCKETS); - int lock_id = m_array[bucket_id].lockId; - - (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); - MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); - - bool found = false; - GPCEntry *entry = (GPCEntry *)hash_search_with_hash_value(m_array[bucket_id].hash_tbl, - (const void*)key, hashCode, HASH_ENTER, &found); - if (entry == NULL) { - MemoryContextSwitchTo(oldcontext); - LWLockRelease(GetMainLWLockByIndex(lock_id)); - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_PSTATEMENT), - errmsg("store global plan source failed due to memory allocation failed"))); - } - - if (found == false) { - /* Deep copy the query_string to the GPC entry's query_string */ - entry->key.query_string = key->query_string; - entry->key.query_length = key->query_length; - entry->key.spi_signature = key->spi_signature; - /* Set the magic number. */ - entry->val.plansource = plansource; - //off the link - plansource->next_saved = NULL; - INSTR_TIME_SET_CURRENT(entry->val.last_use_time); - //initialize the ref count . - plansource->gpc.status.AddRefcount(); - - m_array[bucket_id].count++; - pg_atomic_fetch_add_u32((volatile uint32*)&plansource->gplan->global_refcount, 1); - plansource->gplan->is_share = true; - Assert(plansource->context->is_shared); - MemoryContextSeal(plansource->context); - Assert(plansource->query_context->is_shared); - MemoryContextSeal(plansource->query_context); - Assert(plansource->gplan->context->is_shared); - MemoryContextSeal(plansource->gplan->context); - plansource->gpc.status.SetLoc(GPC_SHARE_IN_SHARE_TABLE); - - } else { - //some guys win. - CachedPlanSource* newsource = entry->val.plansource; - ListCell* n_cell = list_nth_cell(spiplan->plancache_list, nth); - n_cell->data.ptr_value = (void*)newsource; - newsource->gpc.status.AddRefcount(); - - // purge old one. - CN_GPC_LOG("drop cache plan in try store", plansource, 0); - DropCachedPlan(plansource); - CN_GPC_LOG("change to cache plan", newsource, 0); - } - - MemoryContextSwitchTo(oldcontext); - LWLockRelease(GetMainLWLockByIndex(lock_id)); -} - -void GlobalPlanCache::SPICommit(CachedPlanSource* plansource) -{ - Assert (u_sess->SPI_cxt.SPICacheTable != NULL); - plpgsql_SPIPlanCacheEnt* entry = SPIPlanCacheTableLookup(plansource->spi_signature.spi_key); - Assert(entry != NULL); - Assert(entry->func_oid != InvalidOid); - List* spiplan_list = entry->SPIplan_list; - ListCell* cell = NULL; - bool has_cachedplan = false; - foreach(cell, spiplan_list) { - SPIPlanPtr spi_plan = (SPIPlanPtr)lfirst(cell); - if (spi_plan->id == plansource->spi_signature.spi_id) { - SPITryStore(plansource, spi_plan, plansource->spi_signature.plansource_id); - has_cachedplan = true; - break; - } - } - Assert(has_cachedplan); - if (unlikely(has_cachedplan == false)) { -#ifdef MEMORY_CONTEXT_CHECKING - ereport(PANIC, -#else - ereport(ERROR, -#endif - (errcode(ERRCODE_UNDEFINED_PSTATEMENT), - errmsg("In gpc spi finish stage, fail to get spi func: %u. hashkey: %u", - plansource->spi_signature.func_oid, plansource->spi_signature.spi_key))); - } - -#ifdef USE_ASSERT_CHECKING - foreach(cell, spiplan_list) { - SPIPlanPtr spi_plan = (SPIPlanPtr)lfirst(cell); - ListCell* cl = NULL; - if (list_length(spi_plan->plancache_list) == 0) - continue; - foreach(cl, spi_plan->plancache_list) { - CachedPlanSource *cur = (CachedPlanSource*)(cl->data.ptr_value); - Assert(cur->magic == CACHEDPLANSOURCE_MAGIC); - } - } -#endif -} - -void GlobalPlanCache::RemovePlanCacheInSPIPlan(SPIPlanPtr plan) -{ - Assert (plan->magic == _SPI_PLAN_MAGIC); - if (list_length(plan->plancache_list) > 0) { - ListCell* cell = NULL; - foreach(cell, plan->plancache_list) { - CachedPlanSource* plansource = (CachedPlanSource*)lfirst(cell); - if (plansource->gpc.status.InShareTable()) { - Assert(plan->saved); - CN_GPC_LOG("drop shared spi plan, subrefcount", plansource, 0); - /* move plansource into invalid list if during delet func */ - if (u_sess->plsql_cxt.is_delete_function) { - GPCKey* gpckey = plansource->gpc.key; - uint32 hashCode = GPCHashFunc((const void *) gpckey, sizeof(*gpckey)); - uint32 bucket_id = GetBucket(hashCode); - int lock_id = m_array[bucket_id].lockId; - (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); - if (!plansource->gpc.status.InShareTableInvalidList()) { - bool found = false; - (void)hash_search(m_array[bucket_id].hash_tbl, (void *)gpckey, HASH_REMOVE, &found); - m_array[bucket_id].count--; - AddInvalidList(plansource); - } - plansource->gpc.status.SubRefCount(); - DropInvalid(); - LWLockRelease(GetMainLWLockByIndex(lock_id)); - } else { - plansource->gpc.status.SubRefCount(); - } - } else { - CN_GPC_LOG("drop unshared spi plan", plansource, 0); - DropCachedPlan(plansource); - } - } - } - if (plan->spi_key != INVALID_SPI_KEY) - SPIPlanCacheTableDeletePlan(plan->spi_key, plan); -} - -void GlobalPlanCache::CleanUpByTime() -{ - List *gpckey_list = NULL; - const int maxlen_gpckey_list = 100; - instr_time curTime; - INSTR_TIME_SET_CURRENT(curTime); - for (uint32 bucket_id = 0; bucket_id < GPC_NUM_OF_BUCKETS; bucket_id++) - { - int lock_id = m_array[bucket_id].lockId; - - /* Step 1: Try to find the code plan cache */ - LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_SHARED); - if (m_array[bucket_id].count == 0) { - LWLockRelease(GetMainLWLockByIndex(lock_id)); - continue; - } - HASH_SEQ_STATUS hash_seq; - GPCEntry *entry = NULL; - CachedPlanSource* cur_plansource = NULL; - - hash_seq_init(&hash_seq, m_array[bucket_id].hash_tbl); - while ((entry = (GPCEntry*)hash_seq_search(&hash_seq)) != NULL) { - if (entry->val.used_count > 0) { - entry->val.last_use_time = curTime; - entry->val.used_count = 0; - continue; - } - cur_plansource = entry->val.plansource; - if (cur_plansource->gpc.status.RefCountZero() && - INSTR_TIME_GET_DOUBLE(curTime) - INSTR_TIME_GET_DOUBLE(entry->val.last_use_time) > - u_sess->attr.attr_common.gpc_clean_timeout) { - GPCKey *dest_gpckey = (GPCKey *)palloc(sizeof(GPCKey)); - GPCKeyDeepCopy(&entry->key, dest_gpckey); - gpckey_list = lappend(gpckey_list, dest_gpckey); - - /* should not be long */ - if (gpckey_list->length >= maxlen_gpckey_list) { - hash_seq_term(&hash_seq); - break; - } - } - } - LWLockRelease(GetMainLWLockByIndex(lock_id)); - - /* Step 2: Try to remove plan cache */ - if (gpckey_list && list_length(gpckey_list) > 0) { - LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); - ListCell* l = NULL; - foreach(l, gpckey_list) { - bool found = false; - GPCKey *key = (GPCKey *)lfirst(l); - GPCEntry *entry = NULL; - entry = (GPCEntry *)hash_search(m_array[bucket_id].hash_tbl, (void *)key, HASH_FIND, &found); - if (entry) { - cur_plansource = entry->val.plansource; - if (cur_plansource->gpc.status.RefCountZero() && - INSTR_TIME_GET_DOUBLE(curTime) - INSTR_TIME_GET_DOUBLE(entry->val.last_use_time) > - u_sess->attr.attr_common.gpc_clean_timeout) { - GPC_LOG("drop shared plancache by time", cur_plansource, cur_plansource->stmt_name); - DropCachedPlanInternal(cur_plansource); - hash_search(m_array[bucket_id].hash_tbl, (void *) key, HASH_REMOVE, &found); - cur_plansource->magic = 0; - MemoryContextUnSeal(cur_plansource->context); - MemoryContextUnSeal(cur_plansource->query_context); - if (cur_plansource->opFusionObj) { - OpFusion::DropGlobalOpfusion((OpFusion*)(cur_plansource->opFusionObj)); - } - MemoryContextDelete(cur_plansource->context); - m_array[bucket_id].count--; - } - } - pfree((void *)key->query_string); - pfree_ext(key->env.param_types); - pfree_ext(key->env.search_path->schemas); - pfree_ext(key->env.search_path); - pfree_ext(key); - } - LWLockRelease(GetMainLWLockByIndex(lock_id)); - - list_free_ext(gpckey_list); - } - } -} - -void CleanSessGPCPtr(knl_session_context* currentSession) -{ - CachedPlanSource *psrc = currentSession->pcache_cxt.cur_stmt_psrc; - currentSession->pcache_cxt.cur_stmt_psrc = NULL; - if (psrc && psrc->magic != CACHEDPLANSOURCE_MAGIC) - elog(PANIC, "cur psrc wrong"); - if (psrc && psrc->gpc.status.InShareTable()) { - psrc->gpc.status.SubRefCount(); - currentSession->pcache_cxt.private_refcount--; - } - if (unlikely(currentSession->pcache_cxt.private_refcount != 0)) - elog(PANIC, "wrong refcount"); -} - -void CleanSessionGPCDetach(knl_session_context* currentSession) -{ - if (IS_PGXC_COORDINATOR) - return; - if (currentSession->pcache_cxt.cur_stmt_psrc != NULL) { - elog(PANIC, "session's cur_stmt_psrc should be null when detach"); - } - - CachedPlanSource* plansource = currentSession->pcache_cxt.first_saved_plan; - while (plansource != NULL) { - /* - * When turing on the enable_global_plancache, there are some cases that - * we cannot insert the plancache in the shared HTAB. No Prepare Statement - * on DN, so we can just drop shared plan and wait for next parse message - * to create it again. - */ - CachedPlanSource* next_plansource = plansource->next_saved; - if (plansource->gpc.status.IsPrivatePlan()) { - /* private plan has reference on pointer like unname_stmt_psrc or spiplan */ - GPC_LOG("invalid plan in sess detach", plansource, plansource->stmt_name); - plansource->is_valid = false; - plansource->next_saved = NULL; - plansource->gpc.status.SetLoc(GPC_SHARE_IN_PREPARE_STATEMENT); - plansource->gpc.status.SetStatus(GPC_INVALID); - } else { - DropCachedPlan(plansource); - } - plansource = next_plansource; - } - - currentSession->pcache_cxt.first_saved_plan = NULL; - currentSession->pcache_cxt.gpc_in_ddl = false; -} - -- 2.34.1 From 360e802abf545fb0b11607906eb3ba2a95450ee1 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:10:08 +0800 Subject: [PATCH 42/56] ADD file via upload --- .../globalplancache/globalplancache.cpp | 1304 +++++++++++++++++ 1 file changed, 1304 insertions(+) create mode 100644 src/gausskernel/process/globalplancache/globalplancache.cpp diff --git a/src/gausskernel/process/globalplancache/globalplancache.cpp b/src/gausskernel/process/globalplancache/globalplancache.cpp new file mode 100644 index 000000000..467d1db60 --- /dev/null +++ b/src/gausskernel/process/globalplancache/globalplancache.cpp @@ -0,0 +1,1304 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * globalplancache.cpp + * global plan cache + * + * IDENTIFICATION + * src/gausskernel/process/globalplancache/globalplancache.cpp + * + * ------------------------------------------------------------------------- + */ + + +#include "postgres.h" +#include "knl/knl_variable.h" + +#include "access/hash.h" +#include "access/xact.h" +#include "catalog/pgxc_node.h" +#include "commands/prepare.h" +#include "executor/lightProxy.h" +#include "executor/spi_priv.h" +#include "optimizer/nodegroups.h" +#include "opfusion/opfusion.h" +#include "pgxc/groupmgr.h" +#include "pgxc/pgxcnode.h" +#include "utils/dynahash.h" +#include "utils/globalplancache.h" +#include "utils/memutils.h" +#include "utils/plancache.h" +#include "utils/syscache.h" +#include "utils/plpgsql.h" +#include "nodes/pg_list.h" +#include "commands/sqladvisor.h" + +template void GlobalPlanCache::RemovePlanSource(CachedPlanSource* plansource, const char* stmt_name); + +template void GlobalPlanCache::RemovePlanSource(CachedPlanSource* plansource, const char* stmt_name); + +template void GlobalPlanCache::RemovePlanSource(CachedPlanSource* plansource, const char* stmt_name); +/* + * 函数名:has_diff_schema + * 功能:判断两个列表是否有不同的schema(OID)。 + * 参数: + * - list1: 第一个列表 + * - list2: 第二个列表 + * 返回值: + * - bool类型,如果list1中有与list2不同的schema,则返回true;否则返回false。 + */ +static bool has_diff_schema(const List *list1, const List *list2) +{ + const ListCell *cell = NULL; + + /* 如果list2为空,则只需判断list1是否非空即可 */ + if (list2 == NIL) { + return list1 != NULL; + } + + /* 遍历list1中的每个元素,如果在list2中找不到对应的OID,表示有不同的schema */ + foreach (cell, list1) { + if (!list_member_oid(list2, lfirst_oid(cell))) { + return true; + } + } + + /* 如果遍历结束后都没有找到不同的schema,返回false */ + return false; +} + +/* + * 函数名:CompareSearchPath + * 功能:比较两个OverrideSearchPath结构体的内容是否相等。 + * 参数: + * - path1: 第一个OverrideSearchPath结构体指针 + * - path2: 第二个OverrideSearchPath结构体指针 + * 返回值: + * - bool类型,如果两个结构体的内容相等,则返回true;否则返回false。 + */ +static bool CompareSearchPath(struct OverrideSearchPath* path1, struct OverrideSearchPath* path2) +{ + Assert(path1 != NULL); + + /* 如果path2为空,则比较path1和当前搜索路径是否相等 */ + if (path2 == NULL) { + return OverrideSearchPathMatchesCurrent(path1); + } + + /* 如果path1和path2是同一个结构体的指针,则认为相等 */ + if (path1 == path2) { + return true; + } + + /* 逐个比较结构体中的字段值 */ + if (path1->addTemp != path2->addTemp) { + return false; + } + if (path1->addCatalog != path2->addCatalog) { + return false; + } + if (has_diff_schema(path1->schemas, path2->schemas)) { + return false; + } + if (has_diff_schema(path2->schemas, path1->schemas)) { + return false; + } + return true; +} + +/* + * 函数名:GPCCompareParam + * 功能:比较两个Oid数组是否相等。 + * 参数: + * - params1: 第一个Oid数组 + * - params2: 第二个Oid数组 + * - paramNum: 数组长度 + * 返回值: + * - bool类型,如果两个数组的内容完全相等,则返回true;否则返回false。 + */ +static bool GPCCompareParam(Oid* params1, Oid* params2, int paramNum) +{ + for (int i = 0; i < paramNum; i++) { + if (params1[i] != params2[i]) { + return false; + } + } + return true; +} +/* + * GPCCompareEnv - 用于比较两个GPCEnv结构体是否相等。 + */ +static bool +GPCCompareEnv(GPCEnv *env1, GPCEnv *env2) +{ + Assert(env1 != NULL); + Assert(env2 != NULL); + // 比较plainenv字段 + if (memcmp(&env1->plainenv, &env2->plainenv, sizeof(GPCPlainEnv)) == 0 + // 比较default_storage_nodegroup字段 + && strncmp(env1->default_storage_nodegroup, env2->default_storage_nodegroup, NAMEDATALEN) == 0 + // 比较expected_computing_nodegroup字段 + && strncmp(env1->expected_computing_nodegroup, env2->expected_computing_nodegroup, NAMEDATALEN) == 0 + // 比较param_types数组中的元素是否相等 + && env1->num_params == env2->num_params) + { + if (!GPCCompareParam(env1->param_types, env2->param_types, env1->num_params)) { + return false; + } + // 比较search_path字段 + if (CompareSearchPath(env1->search_path, env2->search_path)) { + // 如果depends_on_role字段和user_oid字段不相等,则返回false + if (env1->depends_on_role != env2->depends_on_role && env1->user_oid != env2->user_oid) { + return false; + } else if (env1->depends_on_role && env2->depends_on_role && env1->user_oid != env2->user_oid) { + return false; + } else { + return true; + } + } + } + + return false; +} + +/* + * GPCHashFunc - 计算GPCKey结构体的哈希值 + */ +uint32 GPCHashFunc(const void *key, Size keysize) +{ + const GPCKey *item = (const GPCKey *) key; + // 对query_string字段进行哈希计算 + uint32 val1 = DatumGetUInt32(hash_any((const unsigned char *) item->query_string, item->query_length)); + // 对plainenv字段进行哈希计算 + uint32 val2 = DatumGetUInt32(hash_any((const unsigned char *) (&item->env.plainenv), sizeof(GPCPlainEnv))); + // 对spi_signature字段进行哈希计算 + uint32 val3 = DatumGetUInt32(hash_any((const unsigned char *) (&item->spi_signature), sizeof(SPISign))); + // 使用异或运算组合哈希结果 + val1 ^= val2; + val1 ^= val3; + + return val1; +} + +/* + * GPCKeyMatch - 比较两个GPCKey结构体是否相等 + */ +int GPCKeyMatch(const void *left, const void *right, Size keysize) +{ + GPCKey *leftItem = (GPCKey*)left; + GPCKey *rightItem = (GPCKey*)right; + Assert(NULL != leftItem); + Assert(NULL != rightItem); + + // 判断query_length字段是否相等 + if (leftItem->query_length != rightItem->query_length) { + return 1; + } + // 判断query_string字段是否相等 + if (strncmp(leftItem->query_string, rightItem->query_string, leftItem->query_length)) { + return 1; + } + // 比较GPCEnv结构体是否相等 + if (GPCCompareEnv(&(leftItem->env), &(rightItem->env)) == false) { + return 1; + } + + return 0; +} + +/* + * GPCKeyDeepCopy - 创建给定GPCKey结构体的深拷贝 + */ +void GPCKeyDeepCopy(const GPCKey *srcGpckey, GPCKey *destGpckey) +{ + *destGpckey = *srcGpckey; + if (srcGpckey->query_string) + destGpckey->query_string = pstrdup(srcGpckey->query_string); + // 拷贝param_types数组 + if (destGpckey->env.num_params > 0) { + destGpckey->env.param_types = (Oid*) palloc(sizeof(Oid) * destGpckey->env.num_params); + errno_t rc = 0; + rc = memcpy_s(destGpckey->env.param_types, sizeof(Oid) * destGpckey->env.num_params, + srcGpckey->env.param_types, sizeof(Oid) * destGpckey->env.num_params); + securec_check(rc, "", ""); + } + // 拷贝search_path字段 + if (destGpckey->env.schema_name) { + destGpckey->env.search_path = (struct OverrideSearchPath *) palloc(sizeof(struct OverrideSearchPath)); + *destGpckey->env.search_path = *srcGpckey->env.search_path; + destGpckey->env.search_path->schemas = list_copy(srcGpckey->env.search_path->schemas); + } +} + +/***************** + global plan cache + *****************/ +/** + * 构造函数:GlobalPlanCache + */ +GlobalPlanCache::GlobalPlanCache() +{ + Init(); +} + +/** + * 析构函数:~GlobalPlanCache + */ +GlobalPlanCache::~GlobalPlanCache() +{ +} + +/** + * 初始化函数:Init + */ +void GlobalPlanCache::Init() +{ + // 创建 HASHCTL 结构体 ctl + HASHCTL ctl; + errno_t rc = 0; + rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl)); + securec_check(rc, "\0", "\0"); + + // 设置 HASHCTL 结构体的字段值 + ctl.keysize = sizeof(GPCKey); + ctl.entrysize = sizeof(GPCEntry); + ctl.hash = (HashValueFunc)GPCHashFunc; + ctl.match = (HashCompareFunc)GPCKeyMatch; + + int flags = HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_COMPARE | HASH_EXTERN_CONTEXT | HASH_NOEXCEPT; + + // 分配 GPCHashCtl 数组的内存空间 + m_array = (GPCHashCtl *) MemoryContextAllocZero(GLOBAL_PLANCACHE_MEMCONTEXT, + sizeof(GPCHashCtl) * GPC_NUM_OF_BUCKETS); + + // 遍历 GPCHashCtl 数组,进行初始化 + for (uint32 i = 0; i < GPC_NUM_OF_BUCKETS; i++) { + // 初始化 count 字段和 lockId 字段 + m_array[i].count = 0; + m_array[i].lockId = FirstGPCMappingLock + i; + + /* + * 为每个哈希桶创建一个内存上下文,以保证该桶下的所有条目、计划等都在这个内存上下文中。 + * 这样做是为了提高性能。我们不希望所有东西都在共享的 GlobalPlanCacheContext 下, + * 因为每次需要一块内存时,更多的线程需要进行同步,这将成为瓶颈。 + */ + m_array[i].context = AllocSetContextCreate(GLOBAL_PLANCACHE_MEMCONTEXT, + "GPC_Plan_Bucket_Context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE, + SHARED_CONTEXT); + + // 设置 ctl 的 hcxt 字段为当前哈希桶的上下文 + ctl.hcxt = m_array[i].context; + + // 创建哈希表 + m_array[i].hash_tbl = hash_create("Global_Plan_Cache", + GPC_HTAB_SIZE, + &ctl, + flags); + } + + m_invalid_list = NULL; +} + +/** + * 尝试存储函数:TryStore + * @param plansource CachedPlanSource指针 + * @param ps PreparedStatement指针 + * @return bool类型,表示存储是否成功 + */ +bool GlobalPlanCache::TryStore(CachedPlanSource *plansource, PreparedStatement *ps) +{ + Assert (plansource != NULL); + Assert (plansource->magic == CACHEDPLANSOURCE_MAGIC); + Assert (!plansource->gpc.status.InShareTable()); + Assert (plansource->gpc.status.IsSharePlan()); + Assert (plansource->is_support_gplan || (plansource->gplan == NULL && plansource->cplan == NULL)); + + GPCKey* key = plansource->gpc.key; + + uint32 hashCode = GPCHashFunc((const void *) key, sizeof(*key)); + + uint32 bucket_id = GetBucket(hashCode); + Assert (bucket_id >= 0 && bucket_id < GPC_NUM_OF_BUCKETS); + int lock_id = m_array[bucket_id].lockId; + + // 获取锁 + (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); + MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); + + bool found = false; + // 在哈希表中搜索对应的条目 + GPCEntry *entry = (GPCEntry *)hash_search_with_hash_value(m_array[bucket_id].hash_tbl, + (const void*)key, hashCode, HASH_ENTER, &found); + if (entry == NULL) { + MemoryContextSwitchTo(oldcontext); + LWLockRelease(GetMainLWLockByIndex(lock_id)); + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_PSTATEMENT), + errmsg("store global plan source failed due to memory allocation failed"))); + return false; + } + + if (found == false) { + START_CRIT_SECTION(); + /* 深拷贝 query_string 到 GPC 条目的 query_string 字段 */ + entry->key.query_string = key->query_string; + entry->key.query_length = key->query_length; + /* 设置 magic number */ + entry->val.plansource = plansource; + entry->val.used_count = 0; + INSTR_TIME_SET_CURRENT(entry->val.last_use_time); + /* 关闭链路 */ + plansource->next_saved = NULL; + plansource->is_checked_opfusion = true; + if (plansource->opFusionObj != NULL) { + OpFusion::SaveInGPC((OpFusion*)(plansource->opFusionObj)); + } + /* 初始化引用计数 */ +#ifdef ENABLE_MULTIPLE_NODES + /* 数据节点仅对 cur_stmt_psrc 进行引用计数,不对预处理语句进行计数。 + 协调器对预处理语句进行引用计数。 */ + if (IS_PGXC_COORDINATOR) + plansource->gpc.status.AddRefcount(); +#else + plansource->gpc.status.AddRefcount(); +#endif + m_array[bucket_id].count++; + Assert(plansource->context->is_shared); + MemoryContextSeal(plansource->context); + Assert(plansource->query_context->is_shared); + MemoryContextSeal(plansource->query_context); + if (plansource->gplan) { + pg_atomic_fetch_add_u32((volatile uint32*)&plansource->gplan->global_refcount, 1); + plansource->gplan->is_share = true; + Assert(plansource->gplan->context->is_shared); + MemoryContextSeal(plansource->gplan->context); + } +#ifdef USE_ASSERT_CHECKING + else { + Assert(IS_PGXC_COORDINATOR && plansource->single_exec_node && + plansource->gplan == NULL && plansource->cplan == NULL); + } +#endif + plansource->gpc.status.SetLoc(GPC_SHARE_IN_SHARE_TABLE); + + END_CRIT_SECTION(); + + } else { + /* 有其他进程获取到了存储的权利 */ + if (ps == NULL) { + Assert (IS_PGXC_DATANODE); + GPC_LOG("drop cache plan in try store", plansource, 0); + DropCachedPlan(plansource); + } else { + CachedPlanSource* newsource = entry->val.plansource; + ps->plansource = newsource; + newsource->gpc.status.AddRefcount(); + INSTR_TIME_SET_CURRENT(entry->val.last_use_time); + u_sess->pcache_cxt.gpc_in_try_store = true; + + /* 清除旧的计划 */ + GPC_LOG("drop cache plan in try store", plansource, 0); +#ifdef ENABLE_MULTIPLE_NODES + if (IS_PGXC_COORDINATOR) { + GPCDropLPIfNecessary(ps->stmt_name, false, false, newsource); + } +#endif + DropCachedPlan(plansource); + u_sess->pcache_cxt.gpc_in_try_store = false; + } + } + + // 切换回旧的内存上下文,并释放锁 + MemoryContextSwitchTo(oldcontext); + LWLockRelease(GetMainLWLockByIndex(lock_id)); + return true; +} + +CachedPlanSource* GlobalPlanCache::Fetch(const char *query_string, uint32 query_len, + int num_params, Oid* paramTypes, SPISign* spi_sign_ptr) +{ + // 构造缓存键 + GPCKey key; + key.env.filled = false; + key.query_string = query_string; + key.query_length = query_len; + EnvFill(&key.env, false); + key.env.search_path = NULL; + key.env.num_params = num_params; + key.env.param_types = paramTypes; + if (spi_sign_ptr != NULL) + key.spi_signature = *spi_sign_ptr; + else + key.spi_signature = {(uint32)-1, 0, (uint32)-1, -1}; + + // 计算哈希值和桶ID + uint32 hashCode = GPCHashFunc((const void *) &key, sizeof(key)); + uint32 bucket_id = GetBucket(hashCode); + Assert (bucket_id >= 0 && bucket_id < GPC_NUM_OF_BUCKETS); + int lock_id = m_array[bucket_id].lockId; + + // 获取读锁,并切换内存上下文 + (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_SHARED); + MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); + + bool foundCachedEntry = false; + // 在哈希表中查找缓存的计划 + GPCEntry *entry = (GPCEntry *) hash_search_with_hash_value(m_array[bucket_id].hash_tbl, + (const void*)(&key), + hashCode, + HASH_FIND, + &foundCachedEntry); + + if (!foundCachedEntry) { + // 未找到缓存的计划 + MemoryContextSwitchTo(oldcontext); + LWLockRelease(GetMainLWLockByIndex(lock_id)); + return NULL; + } else { + // 找到缓存的计划 + CachedPlanSource* psrc = entry->val.plansource; + psrc->gpc.status.AddRefcount(); // 计数加一,增加引用计数 + if (!psrc->gpc.status.IsValid()) { + // 缓存的计划无效,将其移动到无效列表中 + MemoryContextSwitchTo(oldcontext); + LWLockRelease(GetMainLWLockByIndex(lock_id)); + MoveIntoInvalidPlanList(psrc); + psrc->gpc.status.SubRefCount(); // 计数减一,减少引用计数 + return NULL; + } + if (ENABLE_DN_GPC) + u_sess->pcache_cxt.private_refcount++; + pg_atomic_fetch_add_u32(&entry->val.used_count, 1); // 增加计划的使用计数 + MemoryContextSwitchTo(oldcontext); + LWLockRelease(GetMainLWLockByIndex(lock_id)); + return psrc; + } + + return NULL; +} + +void GlobalPlanCache::AddInvalidList(CachedPlanSource* plansource) +{ + // 获取清理锁,并切换内存上下文 + (void)LWLockAcquire(GPCClearLock, LW_EXCLUSIVE); + MemoryContext oldcontext = MemoryContextSwitchTo(GLOBAL_PLANCACHE_MEMCONTEXT); + START_CRIT_SECTION(); + // 设置计划的状态为在共享表的无效列表中 + plansource->gpc.status.SetLoc(GPC_SHARE_IN_SHARE_TABLE_INVALID_LIST); + m_invalid_list = dlappend(m_invalid_list, plansource); // 将计划添加到无效列表中 + plansource->gpc.status.SetStatus(GPC_INVALID); // 设置计划为无效状态 + END_CRIT_SECTION(); + MemoryContextSwitchTo(oldcontext); + LWLockRelease(GPCClearLock); +} + +void GlobalPlanCache::DropInvalid() +{ + // 获取清理锁 + (void)LWLockAcquire(GPCClearLock, LW_EXCLUSIVE); + if (m_invalid_list != NULL) { + DListCell *cell = m_invalid_list->head; + while (cell != NULL) { + CachedPlanSource *curr = (CachedPlanSource *)cell->data.ptr_value; + if (curr->gpc.status.RefCountZero()) { + Assert(curr->next_saved == NULL); + DListCell *next = cell->next; + GPC_LOG("drop invalid shared plancache", curr, curr->stmt_name); + // 从无效列表中移除计划,并执行相关清理操作 + m_invalid_list = dlist_delete_cell(m_invalid_list, cell, false); + DropCachedPlanInternal(curr); + curr->magic = 0; + MemoryContextUnSeal(curr->context); + MemoryContextUnSeal(curr->query_context); + if (curr->opFusionObj) { + OpFusion::DropGlobalOpfusion((OpFusion*)(curr->opFusionObj)); + } + MemoryContextDelete(curr->context); + + cell = next; + } else { + cell = cell->next; + } + } + } + LWLockRelease(GPCClearLock); +} + +template +void GlobalPlanCache::RemovePlanSource(CachedPlanSource* plansource, const char* stmt_name) +{ + Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); + + if(plansource->gpc.status.InShareTable()) { + GPCKey* key = plansource->gpc.key; + uint32 hashCode = GPCHashFunc((const void *) key, sizeof(*key)); + uint32 bucket_id = GetBucket(hashCode); + int lock_id = m_array[bucket_id].lockId; + (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); + if (plansource->gpc.status.InShareTableInvalidList() == false) { + bool found = false; + hash_search(m_array[bucket_id].hash_tbl, (void *)key, HASH_REMOVE, &found); + if (unlikely(found == false)) + elog(PANIC, "should found plan in gpc when RemovePlanSource"); + m_array[bucket_id].count--; + AddInvalidList(plansource); + } + /* Has hold refcount for ACTION_RECREATE */ + if (action_type == ACTION_RECREATE) + plansource->gpc.status.SubRefCount(); + DropInvalid(); + LWLockRelease(GetMainLWLockByIndex(lock_id)); + + if (ENABLE_DN_GPC) { + u_sess->pcache_cxt.private_refcount--; + if (u_sess->pcache_cxt.private_refcount != 0) + elog(PANIC, "wrong refcount in subrefcount"); + } + + if (action_type == ACTION_RELOAD) { + /* clear Datanode statements */ +#ifdef ENABLE_MULTIPLE_NODES + if (plansource->gplan != NULL) + GPCCleanDatanodeStatement(plansource->gplan->dn_stmt_num, stmt_name); + else + GPCDropLPIfNecessary(stmt_name, true, true, NULL); +#endif + } + + } else { + if (action_type == ACTION_RECREATE) { + GPC_LOG("remove private plansource", plansource, plansource->stmt_name); + DropCachedPlan(plansource); + } else { + CN_GPC_LOG("invalid plan", plansource, stmt_name); + plansource->gpc.status.SetStatus(GPC_INVALID); + plansource->is_valid = false; + if (plansource->gplan) { + plansource->gplan->is_valid = false; + Assert(!plansource->gplan->isShared()); + } + if (action_type == ACTION_RELOAD) { + DropCachedPlanInternal(plansource); + if (plansource->gplan == NULL) + GPCDropLPIfNecessary(stmt_name, true, true, NULL); + } + } + } +} + +void GlobalPlanCache::RemoveEntry(uint32 htblIdx, GPCEntry *entry) +{ + CachedPlanSource *plansource = entry->val.plansource; + + bool found = false; + hash_search(m_array[htblIdx].hash_tbl, (void *) &(entry->key), HASH_REMOVE, &found); + Assert(found == true); + m_array[htblIdx].count--; + AddInvalidList(plansource); + DropInvalid(); +} + + + +bool GlobalPlanCache::CheckRecreateCachePlan(CachedPlanSource* psrc, bool* hasGetLock) +{ + /* + * Start up a transaction command so we can run parse analysis etc. (Note + * that this will normally change current memory context.) Nothing happens + * if we are already in one. + */ + start_xact_command(); + Assert(psrc->magic == CACHEDPLANSOURCE_MAGIC); + + // 获取锁以检查计划是否有效,如果需要重新创建计划,则释放锁 + if (psrc->gpc.status.InShareTable()) { + AcquirePlannerLocks(psrc->query_list, true); + if (psrc->gplan) { + AcquireExecutorLocks(psrc->gplan->stmt_list, true); + } + *hasGetLock = true; + } + +#ifdef ENABLE_MULTIPLE_NODES + if (IS_PGXC_COORDINATOR && !psrc->gpc.status.InShareTable()) { + return false; + } +#else + if (!psrc->gpc.status.InShareTable()) { + return false; + } +#endif + + // GPC正在执行DDL + if (u_sess->pcache_cxt.gpc_in_ddl == true) { + return true; + } + // GPC的计划无效 + if (!psrc->gpc.status.IsValid()) { + return true; + } + // GPC依赖角色,但当前角色不一致 + if (psrc->dependsOnRole && (psrc->rewriteRoleId != GetUserId())) { + return true; + } + // GPC的计划为僵化计划 + if ((psrc->gplan != NULL && TransactionIdIsValid(psrc->gplan->saved_xmin))) { + return true; + } + + // GPC的搜索路径已改变 + if (psrc->search_path && !OverrideSearchPathMatchesCurrent(psrc->search_path)) { + return true; + } + + return false; +} + +bool GlobalPlanCache::CheckRecreateSPICachePlan(SPIPlanPtr spi_plan) +{ + ListCell* cell = NULL; + Assert(spi_plan->magic == _SPI_PLAN_MAGIC); + foreach(cell, spi_plan->plancache_list) { + CachedPlanSource* plansource = (CachedPlanSource*)lfirst(cell); + bool hasGetLock = false; + if (CheckRecreateCachePlan(plansource, &hasGetLock)) { + if (hasGetLock) { + AcquirePlannerLocks(plansource->query_list, false); + if (plansource->gplan) { + AcquireExecutorLocks(plansource->gplan->stmt_list, false); + } + } + return true; + } + } + return false; +} + +void GlobalPlanCache::RecreateSPICachePlan(SPIPlanPtr spiplan) +{ + ListCell* cell = NULL; + Assert(spiplan->magic == _SPI_PLAN_MAGIC); + + // push错误上下文栈 + ErrorContextCallback spi_err_context; + spi_err_context.callback = _SPI_error_callback; + spi_err_context.arg = NULL; + spi_err_context.previous = t_thrd.log_cxt.error_context_stack; + t_thrd.log_cxt.error_context_stack = &spi_err_context; + + foreach(cell, spiplan->plancache_list) { + CachedPlanSource* oldsource = (CachedPlanSource*)lfirst(cell); + if (!oldsource->gpc.status.InShareTable()) + continue; + GPC_LOG("recreate spi cachedplan", oldsource, 0); + RecreateCachePlan(oldsource, NULL, NULL, spiplan, cell, false); + } + + // pop错误上下文栈 + t_thrd.log_cxt.error_context_stack = spi_err_context.previous; + Assert(SPIPlanCacheTableLookup(u_sess->SPI_cxt._current->spi_hash_key)); +} + +void GlobalPlanCache::MoveIntoInvalidPlanList(CachedPlanSource* psrc) +{ + if (psrc->gpc.status.InShareTable() && !psrc->gpc.status.IsValid()) { + GPCKey* key = psrc->gpc.key; + uint32 hashCode = GPCHashFunc((const void *) key, sizeof(*key)); + uint32 bucket_id = GetBucket(hashCode); + int lock_id = m_array[bucket_id].lockId; + (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); + if (psrc->gpc.status.InShareTableInvalidList() == false) { + bool found = false; + hash_search(m_array[bucket_id].hash_tbl, (void *)key, HASH_REMOVE, &found); + if (unlikely(found == false)) + elog(PANIC, "should found plan in gpc"); + m_array[bucket_id].count--; + AddInvalidList(psrc); + } + LWLockRelease(GetMainLWLockByIndex(lock_id)); + } +} + +void GlobalPlanCache::RecreateCachePlan(CachedPlanSource* oldsource, const char* stmt_name, PreparedStatement *entry, + SPIPlanPtr spiplan, ListCell* spiplanCell, bool hasGetLock) +{ + GPC_LOG("recreate plan", oldsource, oldsource->stmt_name); + + // 这些操作可能会抛出错误,确保共享计划无效 + CachedPlanSource *newsource = NULL; + PG_TRY(); + { + if (hasGetLock) { + AcquirePlannerLocks(oldsource->query_list, false); + if (oldsource->gplan) { + AcquireExecutorLocks(oldsource->gplan->stmt_list, false); + } + } + newsource = CopyCachedPlan(oldsource, true); + MemoryContext oldcxt = MemoryContextSwitchTo(newsource->context); + newsource->stream_enabled = IsStreamSupport(); + u_sess->exec_cxt.CurrentOpFusionObj = NULL; + Assert (oldsource->gpc.status.IsSharePlan()); + newsource->gpc.status.ShareInit(); + // 如果计划源设置为无效,则必须重新分析AST,因为元数据已更改。 + newsource->is_valid = false; + bool has_lp = false; + + if (spiplan != NULL) { + t_thrd.log_cxt.error_context_stack->arg = (void *)newsource->query_string; + newsource->spi_signature = oldsource->spi_signature; + newsource->parserSetup = spiplan->parserSetup; + newsource->parserSetupArg = spiplan->parserSetupArg; + } else if (IS_PGXC_DATANODE) { + newsource->stmt_name = pstrdup(stmt_name); + } else { + newsource->stmt_name = pstrdup(stmt_name); +#ifdef ENABLE_MULTIPLE_NODES + has_lp = (oldsource->single_exec_node != NULL && oldsource->gplan == NULL && oldsource->cplan == NULL); + // 在CN上清除会话的Datanode语句 + if (has_lp) { + // 新的计划中没有LP,删除旧的LP + GPCDropLPIfNecessary(stmt_name, false, true, NULL); + } else if (oldsource->gplan != NULL) { + // 关闭任何活动的计划DN语句,在BuildCachedPlan中重新创建 + GPCCleanDatanodeStatement(oldsource->gplan->dn_stmt_num, stmt_name); + } +#endif + } + (void)RevalidateCachedQuery(newsource, has_lp); + MemoryContextSwitchTo(oldcxt); + } + PG_CATCH(); + { + // 仅在出现错误时将无效计划源移动到GPC无效列表中 + MoveIntoInvalidPlanList(oldsource); + PG_RE_THROW(); + } + PG_END_TRY(); + + // 新计划已引用会话,忘记资源所有者 + ResourceOwnerForgetGMemContext(t_thrd.utils_cxt.TopTransactionResourceOwner, newsource->context); + newsource->next_saved = u_sess->pcache_cxt.first_saved_plan; + u_sess->pcache_cxt.first_saved_plan = newsource; + newsource->is_saved = true; + newsource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_SAVE_PLAN_LIST); + if (spiplan != NULL) + spiplanCell->data.ptr_value = newsource; + else { +#ifdef ENABLE_MULTIPLE_NODES + if (IS_PGXC_DATANODE) + u_sess->pcache_cxt.cur_stmt_psrc = newsource; + else + entry->plansource = newsource; +#else + entry->plansource = newsource; +#endif + } + + RemovePlanSource(oldsource, stmt_name); +} + +void GlobalPlanCache::Commit() +{ +#ifdef ENABLE_MULTIPLE_NODES + if (IS_PGXC_COORDINATOR) { + CNCommit(); + } else { + DNCommit(); + } +#else + CNCommit(); +#endif +} + +/* + * @Description: Store the global plancache into the hash table and mark them as shared. + * this function called when a transction commited. All unsaved global plancaches are stored + * in the list named first_saved_plan. When we found that a plancache was saved by others, we + * just drop it and update the prepare pointer to the shared glboal plancache. + * @in num: void + * @return - void + */// 全局计划缓存类的DNCommit方法 +void GlobalPlanCache::DNCommit() +{ + CachedPlanSource *next_plansource = NULL; + CachedPlanSource *plansource = u_sess->pcache_cxt.first_saved_plan; + u_sess->pcache_cxt.first_saved_plan = NULL; + CleanSessGPCPtr(u_sess); + + // 检查引用计数是否正确 + if (u_sess->pcache_cxt.private_refcount != 0) { + elog(PANIC, "wrong refcount"); + } + + while (plansource != NULL) { + // 断言检查magic字段和状态 + Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); + Assert(!plansource->gpc.status.InShareTable()); + + // 如果magic字段异常,则报错 + if (unlikely(plansource->magic != CACHEDPLANSOURCE_MAGIC)) { + ereport(PANIC, + (errcode(ERRCODE_UNDEFINED_PSTATEMENT), + errmsg("In gpc commit stage, plansource has already been freed"))); + } + + next_plansource = plansource->next_saved; + + // 处理私有计划 + if (plansource->gpc.status.IsPrivatePlan()) { + // 无效的计划,设置状态为GPC_INVALID并标记为不可用 + GPC_LOG("invalid plan in commit", plansource, plansource->stmt_name); + plansource->is_valid = false; + plansource->next_saved = NULL; + plansource->gpc.status.SetLoc(GPC_SHARE_IN_PREPARE_STATEMENT); + plansource->gpc.status.SetStatus(GPC_INVALID); + } + // 非法计划或者不支持全局计划缓存,则删除缓存 + else if (!plansource->is_valid || plansource->gplan == NULL || !plansource->is_support_gplan) { + GPC_LOG("drop plan in commit", plansource, plansource->stmt_name); + DropCachedPlan(plansource); + } + // 存储计划 + else { + TryStore(plansource, NULL); + } + + plansource = next_plansource; + } +} + +// 全局计划缓存类的CNCommit方法 +void GlobalPlanCache::CNCommit() +{ + CachedPlanSource *next_plansource = NULL; + CachedPlanSource *plansource = u_sess->pcache_cxt.first_saved_plan; + u_sess->pcache_cxt.first_saved_plan = NULL; + + while (plansource != NULL) { + // 断言检查magic字段和状态 + Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); + Assert(!plansource->gpc.status.InShareTable()); + + // 如果magic字段异常,则报错 + if (unlikely(plansource->magic != CACHEDPLANSOURCE_MAGIC)) { + ereport(PANIC, + (errcode(ERRCODE_UNDEFINED_PSTATEMENT), + errmsg("In gpc commit stage, plansource has already been freed"))); + } + + next_plansource = plansource->next_saved; + bool has_lp = false; + + // 判断是否存在轻量级代理计划 +#ifdef ENABLE_MULTIPLE_NODES + has_lp = plansource->single_exec_node && + plansource->gplan == NULL && plansource->cplan == NULL && plansource->stmt_name; + if (has_lp) { + has_lp = (lightProxy::locateLpByStmtName(plansource->stmt_name) != NULL); + } +#endif + + // 非共享计划或者存在cplan,则放入ungpc_save_plan列表 + if (!plansource->gpc.status.IsSharePlan() || (plansource->gplan == NULL && plansource->cplan)) { + plansource->is_saved = true; + if (!plansource->is_support_gplan && plansource->gpc.status.IsSharePlan()) + plansource->gpc.status.SetKind(GPC_CPLAN); + Assert (!plansource->gpc.status.IsSharePlan()); + plansource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_UNGPC_PLAN_LIST); + plansource->next_saved = u_sess->pcache_cxt.ungpc_saved_plan; + u_sess->pcache_cxt.ungpc_saved_plan = plansource; + } + // 非法计划或者全局计划缓存为空(gplan和cplan都为NULL)的情况下,将计划放入保存列表 + else if (!plansource->is_valid || (plansource->gplan && !plansource->gplan->is_valid)) { + plansource->is_valid = false; + plansource->is_saved = true; + Assert (plansource->gpc.status.IsSharePlan()); + plansource->gpc.status.SetStatus(GPC_INVALID); + plansource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_SAVE_PLAN_LIST); + plansource->next_saved = u_sess->pcache_cxt.first_saved_plan; + u_sess->pcache_cxt.first_saved_plan = plansource; + } + // gplan和cplan都为NULL,并且不存在轻量级代理计划时,将计划放入保存列表 + else if (plansource->gplan == NULL && plansource->cplan == NULL && !has_lp) { + plansource->is_saved = true; + Assert (plansource->gpc.status.IsSharePlan()); + plansource->gpc.status.SetLoc(GPC_SHARE_IN_LOCAL_SAVE_PLAN_LIST); + plansource->next_saved = u_sess->pcache_cxt.first_saved_plan; + u_sess->pcache_cxt.first_saved_plan = plansource; + } + // 处理SPI计划 + else { + if (plansource->spi_signature.spi_key != INVALID_SPI_KEY) { + Assert(!has_lp); + Assert(plansource->gplan); + Assert(plansource->is_support_gplan); + g_instance.plan_cache->SPICommit(plansource); + } + // 获取预处理语句并存储计划 + else { + PreparedStatement* ps = FetchPreparedStatement(plansource->stmt_name, true, false); + if (unlikely(ps == NULL)) { +#ifdef MEMORY_CONTEXT_CHECKING + ereport(PANIC, +#else + ereport(ERROR, +#endif + (errcode(ERRCODE_UNDEFINED_PSTATEMENT), + errmsg("In gpc commit stage, fail to fetch prepare statement:%s", plansource->stmt_name))); + } + TryStore(plansource, ps); + } + } + + plansource = next_plansource; + } +} +void GlobalPlanCache::SPITryStore(CachedPlanSource* plansource, SPIPlanPtr spiplan, int nth) +{ + // 尝试将计划存储在全局计划缓存中 + + Assert(plansource != NULL); + Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); + Assert(!plansource->gpc.status.InShareTable()); + Assert(plansource->gpc.status.IsSharePlan()); + Assert(spiplan->saved); + + GPCKey* key = plansource->gpc.key; + + uint32 hashCode = GPCHashFunc((const void*)key, sizeof(*key)); + + uint32 bucket_id = GetBucket(hashCode); + Assert(bucket_id >= 0 && bucket_id < GPC_NUM_OF_BUCKETS); + int lock_id = m_array[bucket_id].lockId; + + (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); + MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); + + bool found = false; + GPCEntry* entry = (GPCEntry*)hash_search_with_hash_value(m_array[bucket_id].hash_tbl, + (const void*)key, hashCode, HASH_ENTER, &found); + if (entry == NULL) { + MemoryContextSwitchTo(oldcontext); + LWLockRelease(GetMainLWLockByIndex(lock_id)); + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_PSTATEMENT), + errmsg("由于内存分配失败,无法存储全局计划源"))); + } + + if (found == false) { + // 深拷贝查询字符串到GPC条目的查询字符串 + entry->key.query_string = key->query_string; + entry->key.query_length = key->query_length; + entry->key.spi_signature = key->spi_signature; + + entry->val.plansource = plansource; // 设置计划源 + plansource->next_saved = NULL; // 关闭链接 + INSTR_TIME_SET_CURRENT(entry->val.last_use_time); // 初始化最后使用时间 + plansource->gpc.status.AddRefcount(); // 初始化引用计数 + + m_array[bucket_id].count++; // 增加计数 + pg_atomic_fetch_add_u32((volatile uint32*)&plansource->gplan->global_refcount, 1); // 计划全局引用计数增加 + plansource->gplan->is_share = true; + Assert(plansource->context->is_shared); + MemoryContextSeal(plansource->context); + Assert(plansource->query_context->is_shared); + MemoryContextSeal(plansource->query_context); + Assert(plansource->gplan->context->is_shared); + MemoryContextSeal(plansource->gplan->context); + plansource->gpc.status.SetLoc(GPC_SHARE_IN_SHARE_TABLE); + + } else { + // 有其他进程已经存储了计划 + CachedPlanSource* newsource = entry->val.plansource; + ListCell* n_cell = list_nth_cell(spiplan->plancache_list, nth); + n_cell->data.ptr_value = (void*)newsource; + newsource->gpc.status.AddRefcount(); + + // 清除旧的计划 + CN_GPC_LOG("在尝试存储时删除已缓存的计划", plansource, 0); + DropCachedPlan(plansource); + CN_GPC_LOG("改为使用缓存的计划", newsource, 0); + } + + MemoryContextSwitchTo(oldcontext); + LWLockRelease(GetMainLWLockByIndex(lock_id)); +} + +void GlobalPlanCache::SPICommit(CachedPlanSource* plansource) +{ + // 提交SPI计划 + + Assert(u_sess->SPI_cxt.SPICacheTable != NULL); + plpgsql_SPIPlanCacheEnt* entry = SPIPlanCacheTableLookup(plansource->spi_signature.spi_key); + Assert(entry != NULL); + Assert(entry->func_oid != InvalidOid); + List* spiplan_list = entry->SPIplan_list; + ListCell* cell = NULL; + bool has_cachedplan = false; + foreach(cell, spiplan_list) { + SPIPlanPtr spi_plan = (SPIPlanPtr)lfirst(cell); + if (spi_plan->id == plansource->spi_signature.spi_id) { + SPITryStore(plansource, spi_plan, plansource->spi_signature.plansource_id); + has_cachedplan = true; + break; + } + } + Assert(has_cachedplan); + if (unlikely(has_cachedplan == false)) { +#ifdef MEMORY_CONTEXT_CHECKING + ereport(PANIC, +#else + ereport(ERROR, +#endif + (errcode(ERRCODE_UNDEFINED_PSTATEMENT), + errmsg("在gpc spi完成阶段,无法获取spi函数: %u. hashkey: %u", + plansource->spi_signature.func_oid, plansource->spi_signature.spi_key))); + } + +#ifdef USE_ASSERT_CHECKING + foreach(cell, spiplan_list) { + SPIPlanPtr spi_plan = (SPIPlanPtr)lfirst(cell); + ListCell* cl = NULL; + if (list_length(spi_plan->plancache_list) == 0) + continue; + foreach(cl, spi_plan->plancache_list) { + CachedPlanSource* cur = (CachedPlanSource*)(cl->data.ptr_value); + Assert(cur->magic == CACHEDPLANSOURCE_MAGIC); + } + } +#endif +} +/** + * 从SPIPlan中移除计划缓存 + */ +void GlobalPlanCache::RemovePlanCacheInSPIPlan(SPIPlanPtr plan) +{ + Assert(plan->magic == _SPI_PLAN_MAGIC); + + if (list_length(plan->plancache_list) > 0) { + ListCell* cell = NULL; + foreach(cell, plan->plancache_list) { + CachedPlanSource* plansource = (CachedPlanSource*)lfirst(cell); + + // 检查计划是否在共享表中 + if (plansource->gpc.status.InShareTable()) { + Assert(plan->saved); + CN_GPC_LOG("drop shared spi plan, subrefcount", plansource, 0); + + // 如果正在删除函数,则将plansource移动到无效列表中 + if (u_sess->plsql_cxt.is_delete_function) { + GPCKey* gpckey = plansource->gpc.key; + uint32 hashCode = GPCHashFunc((const void *) gpckey, sizeof(*gpckey)); + uint32 bucket_id = GetBucket(hashCode); + int lock_id = m_array[bucket_id].lockId; + (void)LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); + + // 如果plansource不在无效列表中,则将其从哈希表中移除,并将其添加到无效列表中 + if (!plansource->gpc.status.InShareTableInvalidList()) { + bool found = false; + (void)hash_search(m_array[bucket_id].hash_tbl, (void *)gpckey, HASH_REMOVE, &found); + m_array[bucket_id].count--; + AddInvalidList(plansource); + } + + plansource->gpc.status.SubRefCount(); + DropInvalid(); + LWLockRelease(GetMainLWLockByIndex(lock_id)); + } else { + plansource->gpc.status.SubRefCount(); + } + } else { + CN_GPC_LOG("drop unshared spi plan", plansource, 0); + DropCachedPlan(plansource); + } + } + } + + if (plan->spi_key != INVALID_SPI_KEY) + SPIPlanCacheTableDeletePlan(plan->spi_key, plan); +} + +/** + * 根据时间进行清理 + */ +void GlobalPlanCache::CleanUpByTime() +{ + List *gpckey_list = NULL; + const int maxlen_gpckey_list = 100; + instr_time curTime; + INSTR_TIME_SET_CURRENT(curTime); + + for (uint32 bucket_id = 0; bucket_id < GPC_NUM_OF_BUCKETS; bucket_id++) + { + int lock_id = m_array[bucket_id].lockId; + + // 步骤1:尝试找到计划缓存 + LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_SHARED); + if (m_array[bucket_id].count == 0) { + LWLockRelease(GetMainLWLockByIndex(lock_id)); + continue; + } + HASH_SEQ_STATUS hash_seq; + GPCEntry *entry = NULL; + CachedPlanSource* cur_plansource = NULL; + + hash_seq_init(&hash_seq, m_array[bucket_id].hash_tbl); + + // 遍历哈希表中的每个项 + while ((entry = (GPCEntry*)hash_seq_search(&hash_seq)) != NULL) { + + // 如果计划被使用过,则更新最后使用时间和使用次数 + if (entry->val.used_count > 0) { + entry->val.last_use_time = curTime; + entry->val.used_count = 0; + continue; + } + + cur_plansource = entry->val.plansource; + + // 如果计划的引用计数为0且超过了清理超时阈值,则将其加入gpckey_list中 + if (cur_plansource->gpc.status.RefCountZero() && + INSTR_TIME_GET_DOUBLE(curTime) - INSTR_TIME_GET_DOUBLE(entry->val.last_use_time) > + u_sess->attr.attr_common.gpc_clean_timeout) { + + GPCKey *dest_gpckey = (GPCKey *)palloc(sizeof(GPCKey)); + GPCKeyDeepCopy(&entry->key, dest_gpckey); + gpckey_list = lappend(gpckey_list, dest_gpckey); + + // 列表长度达到最大值时,结束遍历 + if (gpckey_list->length >= maxlen_gpckey_list) { + hash_seq_term(&hash_seq); + break; + } + } + } + LWLockRelease(GetMainLWLockByIndex(lock_id)); + + // 步骤2:尝试移除计划缓存 + if (gpckey_list && list_length(gpckey_list) > 0) { + LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); + ListCell* l = NULL; + foreach(l, gpckey_list) { + bool found = false; + GPCKey *key = (GPCKey *)lfirst(l); + GPCEntry *entry = NULL; + entry = (GPCEntry *)hash_search(m_array[bucket_id].hash_tbl, (void *)key, HASH_FIND, &found); + + if (entry) { + cur_plansource = entry->val.plansource; + + // 如果计划的引用计数为0且超过了清理超时阈值,则移除该计划缓存 + if (cur_plansource->gpc.status.RefCountZero() && + INSTR_TIME_GET_DOUBLE(curTime) - INSTR_TIME_GET_DOUBLE(entry->val.last_use_time) > + u_sess->attr.attr_common.gpc_clean_timeout) { + + GPC_LOG("drop shared plancache by time", cur_plansource, cur_plansource->stmt_name); + DropCachedPlanInternal(cur_plansource); + + hash_search(m_array[bucket_id].hash_tbl, (void *) key, HASH_REMOVE, &found); + cur_plansource->magic = 0; + MemoryContextUnSeal(cur_plansource->context); + MemoryContextUnSeal(cur_plansource->query_context); + + if (cur_plansource->opFusionObj) { + OpFusion::DropGlobalOpfusion((OpFusion*)(cur_plansource->opFusionObj)); + } + + MemoryContextDelete(cur_plansource->context); + m_array[bucket_id].count--; + } + } + + pfree((void *)key->query_string); + pfree_ext(key->env.param_types); + pfree_ext(key->env.search_path->schemas); + pfree_ext(key->env.search_path); + pfree_ext(key); + } + LWLockRelease(GetMainLWLockByIndex(lock_id)); + + list_free_ext(gpckey_list); + } + } +} + +/** + * 清理会话的GPC指针 + */ +void CleanSessGPCPtr(knl_session_context* currentSession) +{ + // 获取当前会话中的当前语句计划 + CachedPlanSource *psrc = currentSession->pcache_cxt.cur_stmt_psrc; + currentSession->pcache_cxt.cur_stmt_psrc = NULL; + + // 检查当前计划是否正确 + if (psrc && psrc->magic != CACHEDPLANSOURCE_MAGIC) + elog(PANIC, "cur psrc wrong"); + + // 如果当前计划在共享表中,则减少引用计数和私有引用计数 + if (psrc && psrc->gpc.status.InShareTable()) { + psrc->gpc.status.SubRefCount(); + currentSession->pcache_cxt.private_refcount--; + } + + // 检查私有引用计数是否为0 + if (unlikely(currentSession->pcache_cxt.private_refcount != 0)) + elog(PANIC, "wrong refcount"); +} + +/** + * 清理会话的GPC指针并分离会话 + */ +void CleanSessionGPCDetach(knl_session_context* currentSession) +{ + // 如果是PGXC_COORDINATOR节点,则直接返回 + if (IS_PGXC_COORDINATOR) + return; + + // 检查会话的当前语句计划是否为NULL + if (currentSession->pcache_cxt.cur_stmt_psrc != NULL) { + elog(PANIC, "session's cur_stmt_psrc should be null when detach"); + } + + CachedPlanSource* plansource = currentSession->pcache_cxt.first_saved_plan; + + // 遍历会话中的每个保存的计划 + while (plansource != NULL) { + + CachedPlanSource* next_plansource = plansource->next_saved; + + // 对于私有计划,将其标记为无效 + if (plansource->gpc.status.IsPrivatePlan()) { + GPC_LOG("invalid plan in sess detach", plansource, plansource->stmt_name); + plansource->is_valid = false; + plansource->next_saved = NULL; + plansource->gpc.status.SetLoc(GPC_SHARE_IN_PREPARE_STATEMENT); + plansource->gpc.status.SetStatus(GPC_INVALID); + } else { + // 对于共享计划,删除计划缓存 + DropCachedPlan(plansource); + } + + plansource = next_plansource; + } + + currentSession->pcache_cxt.first_saved_plan = NULL; + currentSession->pcache_cxt.gpc_in_ddl = false; +} + -- 2.34.1 From d6b4dae70f2a0b8413c9d1fc1e36d6dbc88896e9 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:10:36 +0800 Subject: [PATCH 43/56] Delete 'src/gausskernel/process/globalplancache/globalplancache_inval.cpp' --- .../globalplancache/globalplancache_inval.cpp | 151 ------------------ 1 file changed, 151 deletions(-) delete mode 100644 src/gausskernel/process/globalplancache/globalplancache_inval.cpp diff --git a/src/gausskernel/process/globalplancache/globalplancache_inval.cpp b/src/gausskernel/process/globalplancache/globalplancache_inval.cpp deleted file mode 100644 index 9c2568249..000000000 --- a/src/gausskernel/process/globalplancache/globalplancache_inval.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * globalplancache_inval.cpp - * global plan cache - * - * IDENTIFICATION - * src/gausskernel/process/globalplancache/globalplancache_inval.cpp - * - * ------------------------------------------------------------------------- - */ - -#include "postgres.h" -#include "knl/knl_variable.h" - -#include "access/hash.h" -#include "access/xact.h" -#include "catalog/pgxc_node.h" -#include "commands/prepare.h" -#include "optimizer/nodegroups.h" -#include "pgxc/groupmgr.h" -#include "pgxc/pgxcnode.h" -#include "utils/dynahash.h" -#include "utils/globalplancache.h" -#include "utils/memutils.h" -#include "utils/plancache.h" -#include "utils/syscache.h" - -bool GlobalPlanCache::MsgCheck(const SharedInvalidationMessage *msg) -{ - if (msg->id >= 0) { - if (msg->cc.id == PROCOID || msg->cc.id == NAMESPACEOID || msg->cc.id == OPEROID || msg->cc.id == AMOPOPID) { - return true; - } - } else if (msg->id == SHAREDINVALRELCACHE_ID || msg->id == SHAREDINVALPARTCACHE_ID) { - return true; - } - - return false; -} - -bool GlobalPlanCache::NeedDropEntryByLocalMsg(CachedPlanSource* plansource, int tot, const int *idx, const SharedInvalidationMessage *msgs) -{ - Oid database_id = plansource->gpc.key->env.plainenv.database_id; - - for (int j = 0; j < tot; j++) { - const SharedInvalidationMessage *msg = &msgs[idx[j]]; - if ((plansource)->raw_parse_tree && IsA((plansource)->raw_parse_tree, TransactionStmt)) - continue; - - if (msg->id >= 0) { - - if (msg->cc.dbId == database_id || msg->cc.dbId == InvalidOid) { - if (msg->cc.id == PROCOID) { - CheckInvalItemDependency(plansource, msg->cc.id, msg->cc.hashValue); - } else if (msg->cc.id == NAMESPACEOID || msg->cc.id == OPEROID || msg->cc.id == AMOPOPID) { - ResetPlanCache(plansource); - } - } - } else if (msg->id == SHAREDINVALRELCACHE_ID) { - if (msg->rc.dbId == database_id || msg->rc.dbId == InvalidOid) - { - CheckRelDependency(plansource, msg->rc.relId); - } - } else if (msg->id == SHAREDINVALPARTCACHE_ID) { - if (msg->pc.dbId == database_id || msg->pc.dbId == InvalidOid) { - CheckRelDependency(plansource, msg->pc.partId); - } - } - - if (plansource->gpc.status.NeedDropSharedGPC()) { - return true; - } - } - - return false; - - -} - -void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n) -{ - int *idx = (int *)palloc0(n * sizeof(int)); - int tot = 0; - - for (int i = 0; i < n; i++) { - const SharedInvalidationMessage *msg = &msgs[i]; - - if (MsgCheck(msg)) { - idx[tot++] = i; - } - } - - if (tot == 0) { - pfree_ext(idx); - return ; - } - - /* Go through each bucket in the GPC HTAB and do some invalidation depending on the GPCInvalInfo we got.*/ - for (uint32 bucket_id = 0; bucket_id < GPC_NUM_OF_BUCKETS; bucket_id ++) { - /* Ok so bucket is not empty. Get the bucket S-lock so we can iterate through it. */ - int lock_id = m_array[bucket_id].lockId; - LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); - MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); - - /* Check the number of entries in the bucket again. - * GPC Eviction might have removed the last entry while we were waiting for the shared lock. */ - int bucketEntriesCount = m_array[bucket_id].count; - if (0 == bucketEntriesCount) { - MemoryContextSwitchTo(oldcontext); - LWLockRelease(GetMainLWLockByIndex(lock_id)); - continue; - } - - HASH_SEQ_STATUS hash_seq; - hash_seq_init(&hash_seq, m_array[bucket_id].hash_tbl); - GPCEntry *entry = NULL; - - while ((entry = (GPCEntry *)hash_seq_search(&hash_seq)) != NULL) { - Assert (entry->val.plansource != NULL); - /* for standby mode, Invalid Msg send by xlog thread, but xlog thread didn't set db id into MyDatabaseId. - So we need check each plan's db id by gpc'key in NeedDropEntryByLocalMsg latter */ - if (pmState == PM_RUN && - entry->val.plansource->gpc.key->env.plainenv.database_id != u_sess->proc_cxt.MyDatabaseId) { - continue; - } - - /* Atomic read the number of CachedEnvironment in this entry */ - if(NeedDropEntryByLocalMsg(entry->val.plansource, tot, idx, msgs)) { - RemoveEntry(bucket_id, entry); - } - } - - MemoryContextSwitchTo(oldcontext); - LWLockRelease(GetMainLWLockByIndex(lock_id)); - } - - pfree_ext(idx); -} -- 2.34.1 From 3a01cfd61000fd0cd7bd50ffdc529f9c63fc72b3 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:12:12 +0800 Subject: [PATCH 44/56] ADD file via upload --- .../globalplancache/globalplancache_inval.cpp | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 src/gausskernel/process/globalplancache/globalplancache_inval.cpp diff --git a/src/gausskernel/process/globalplancache/globalplancache_inval.cpp b/src/gausskernel/process/globalplancache/globalplancache_inval.cpp new file mode 100644 index 000000000..273e3be59 --- /dev/null +++ b/src/gausskernel/process/globalplancache/globalplancache_inval.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * globalplancache_inval.cpp + * global plan cache + * + * IDENTIFICATION + * src/gausskernel/process/globalplancache/globalplancache_inval.cpp + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "knl/knl_variable.h" + +#include "access/hash.h" +#include "access/xact.h" +#include "catalog/pgxc_node.h" +#include "commands/prepare.h" +#include "optimizer/nodegroups.h" +#include "pgxc/groupmgr.h" +#include "pgxc/pgxcnode.h" +#include "utils/dynahash.h" +#include "utils/globalplancache.h" +#include "utils/memutils.h" +#include "utils/plancache.h" +#include "utils/syscache.h" + +bool GlobalPlanCache::MsgCheck(const SharedInvalidationMessage *msg) +{ + // 检查共享缓存清理消息的合法性 + if (msg->id >= 0) { + if (msg->cc.id == PROCOID || msg->cc.id == NAMESPACEOID || msg->cc.id == OPEROID || msg->cc.id == AMOPOPID) { + return true; + } + } else if (msg->id == SHAREDINVALRELCACHE_ID || msg->id == SHAREDINVALPARTCACHE_ID) { + return true; + } + + return false; +} + +bool GlobalPlanCache::NeedDropEntryByLocalMsg(CachedPlanSource* plansource, int tot, const int *idx, const SharedInvalidationMessage *msgs) +{ + // 检查本地缓存是否需要被清除 + Oid database_id = plansource->gpc.key->env.plainenv.database_id; + + for (int j = 0; j < tot; j++) { + const SharedInvalidationMessage *msg = &msgs[idx[j]]; + if ((plansource)->raw_parse_tree && IsA((plansource)->raw_parse_tree, TransactionStmt)) + continue; + + if (msg->id >= 0) { + + if (msg->cc.dbId == database_id || msg->cc.dbId == InvalidOid) { + if (msg->cc.id == PROCOID) { + CheckInvalItemDependency(plansource, msg->cc.id, msg->cc.hashValue); + } else if (msg->cc.id == NAMESPACEOID || msg->cc.id == OPEROID || msg->cc.id == AMOPOPID) { + ResetPlanCache(plansource); + } + } + } else if (msg->id == SHAREDINVALRELCACHE_ID) { + if (msg->rc.dbId == database_id || msg->rc.dbId == InvalidOid) + { + CheckRelDependency(plansource, msg->rc.relId); + } + } else if (msg->id == SHAREDINVALPARTCACHE_ID) { + if (msg->pc.dbId == database_id || msg->pc.dbId == InvalidOid) { + CheckRelDependency(plansource, msg->pc.partId); + } + } + + if (plansource->gpc.status.NeedDropSharedGPC()) { + return true; + } + } + + return false; + + +} + +void GlobalPlanCache::InvalMsg(const SharedInvalidationMessage *msgs, int n) +{ + // 处理共享缓存清理消息 + int *idx = (int *)palloc0(n * sizeof(int)); + int tot = 0; + + for (int i = 0; i < n; i++) { + const SharedInvalidationMessage *msg = &msgs[i]; + + if (MsgCheck(msg)) { + idx[tot++] = i; + } + } + + if (tot == 0) { + pfree_ext(idx); + return ; + } + + /* Go through each bucket in the GPC HTAB and do some invalidation depending on the GPCInvalInfo we got.*/ + for (uint32 bucket_id = 0; bucket_id < GPC_NUM_OF_BUCKETS; bucket_id ++) { + /* Ok so bucket is not empty. Get the bucket S-lock so we can iterate through it. */ + int lock_id = m_array[bucket_id].lockId; + LWLockAcquire(GetMainLWLockByIndex(lock_id), LW_EXCLUSIVE); + MemoryContext oldcontext = MemoryContextSwitchTo(m_array[bucket_id].context); + + /* Check the number of entries in the bucket again. + * GPC Eviction might have removed the last entry while we were waiting for the shared lock. */ + int bucketEntriesCount = m_array[bucket_id].count; + if (0 == bucketEntriesCount) { + MemoryContextSwitchTo(oldcontext); + LWLockRelease(GetMainLWLockByIndex(lock_id)); + continue; + } + + HASH_SEQ_STATUS hash_seq; + hash_seq_init(&hash_seq, m_array[bucket_id].hash_tbl); + GPCEntry *entry = NULL; + + while ((entry = (GPCEntry *)hash_seq_search(&hash_seq)) != NULL) { + Assert (entry->val.plansource != NULL); + /* for standby mode, Invalid Msg send by xlog thread, but xlog thread didn't set db id into MyDatabaseId. + So we need check each plan's db id by gpc'key in NeedDropEntryByLocalMsg latter */ + if (pmState == PM_RUN && + entry->val.plansource->gpc.key->env.plainenv.database_id != u_sess->proc_cxt.MyDatabaseId) { + continue; + } + + /* Atomic read the number of CachedEnvironment in this entry */ + if(NeedDropEntryByLocalMsg(entry->val.plansource, tot, idx, msgs)) { + RemoveEntry(bucket_id, entry); + } + } + + MemoryContextSwitchTo(oldcontext); + LWLockRelease(GetMainLWLockByIndex(lock_id)); + } + + pfree_ext(idx); +} -- 2.34.1 From 2e3138ee3b1ce645b100a6a4ec8b59cf9e9326c2 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:13:53 +0800 Subject: [PATCH 45/56] Delete 'src/gausskernel/process/job/gs_job_calendar.cpp' --- .../process/job/gs_job_calendar.cpp | 1524 ----------------- 1 file changed, 1524 deletions(-) delete mode 100644 src/gausskernel/process/job/gs_job_calendar.cpp diff --git a/src/gausskernel/process/job/gs_job_calendar.cpp b/src/gausskernel/process/job/gs_job_calendar.cpp deleted file mode 100644 index a9a8f85a0..000000000 --- a/src/gausskernel/process/job/gs_job_calendar.cpp +++ /dev/null @@ -1,1524 +0,0 @@ -/* - * 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. - * ------------------------------------------------------------------------- - * - * gs_job_calendar.cpp - * Calendaring syntax for dbe jobs. - * - * IDENTIFICATION - * src/gausskernel/process/job/gs_job_calendar.cpp - * - * ------------------------------------------------------------------------- - */ - - #include "postgres.h" - #include "miscadmin.h" - #include "utils/builtins.h" - #include "utils/dbe_scheduler.h" - -/* - * repeat_interval = frequency_clause - * [; interval=?] [; bymonth=?] [; byweekno=?] - * [; byyearday=?] [; bymonthday=?] [; byday=?] - * [; byhour=?] [; byminute=?] [; bysecond=?] - * - * frequency_clause = "FREQ" "=" frequency - * frequency = "YEARLY" | "MONTHLY" | "WEEKLY" | "DAILY" | - * "HOURLY" | "MINUTELY" | "SECONDLY" - * - * Note: - * POSIX time (tm) used in this section has following flags: - * int tm_sec Seconds [0,60]. (with leap second!!) - * int tm_min Minutes [0,59]. - * int tm_hour Hour [0,23]. - * int tm_mday Day of month [1,31]. - * int tm_mon Month of year [0,11]. (not ISO standard!! [1,12]) - * int tm_year Years since 1900. - * int tm_wday Day of week [0,6] (Sunday =0). (not ISO standard!! sun =7) - * int tm_yday Day of year [0,365]. (not ISO standard!! [1,366]) - * int tm_isdst Daylight Savings flag. - * - */ - -/* Initialize calendaring fields */ -static bool IsLegalIntervalStr(const char* str, bool numeric_only = false); -static char *get_calendar_clause_val(char **tokens, const char *clause, bool numeric_only = false); -static char **tokenize_str(char *src, const char *delims, int fields); -static int validate_field_names(char **toks); - -/* - * Interpreter functions - * Interpret calendaring syntax. - */ -static bool get_calendar_freqency(Calendar calendar, char **tokens); -static void get_calendar_n_interval(Calendar calendar, char **tokens); -static char *get_calendar_bymonth_val(Calendar calendar, char **tokens); -static void get_calendar_bymonth(Calendar calendar, char **tokens); -static void get_calendar_byweekno(Calendar calendar, char **tokens); /* unsupported */ -static void get_calendar_byyearday(Calendar calendar, char **tokens); /* unsupported */ -static void get_calendar_bydate(Calendar calendar, char **tokens); /* unsupported */ -static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens); -static void get_calendar_bymonthday(Calendar calendar, char **tokens); -static void get_calendar_byday(Calendar calendar, char **tokens); /* unsupported */ -static char *get_calendar_byhour_val(Calendar calendar, char **tokens); -static void get_calendar_byhour(Calendar calendar, char **tokens); -static char *get_calendar_byminute_val(Calendar calendar, char **tokens); -static void get_calendar_byminute(Calendar calendar, char **tokens); -static char *get_calendar_bysecond_val(Calendar calendar, char **tokens); -static void get_calendar_bysecond(Calendar calendar, char **tokens); -Calendar interpret_calendar_interval(char *calendar_str); /* interpreter main */ - -/* - * Evaluation functions - * Calculate calendaring interval. - */ -static Interval *get_calendar_period(Calendar calendar, int num_of_period = 1); -static void copy_calendar_dates(TimestampTz *timeline, int nvals, int cnt); -static bool validate_calendar_monthday(int year, int month, int mday); -static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_date, TimestampTz date_after); -static bool recheck_calendar_period(Calendar calendar, TimestampTz start_date, TimestampTz next_date); -static int timestamp_cmp_func(const void *dt1, const void *dt2); -static TimestampTz get_next_calendar_period(Calendar calendar, TimestampTz base_date); -static bool find_nearest_calendar_time(Calendar calendar, TimestampTz *timeline, TimestampTz start, int cnt, - TimestampTz *nearest); - -/* Calendaring Interval Calculator */ -static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, TimestampTz *timeline); -static void evaluate_calendar_bymonth(Calendar calendar, TimestampTz *timeline, int *cnt); -static void evaluate_calendar_byweekno(Calendar calendar, TimestampTz *timeline, int *cnt); /* unsupported */ -static void evaluate_calendar_byyearday(Calendar calendar, TimestampTz *timeline, int *cnt); /* unsupported */ -static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timeline, int *cnt); -static void evaluate_calendar_byhour(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */ -static void evaluate_calendar_byminute(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */ -static void evaluate_calendar_bysecond(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */ -static bool evaluate_calendar_period(Calendar calendar, TimestampTz *timeline, TimestampTz *sub_timeline, - TimestampTz start_date, TimestampTz *next_date); -static TimestampTz evaluate_calendar_interval(Calendar calendar, TimestampTz start_date); /* evaluate main */ - -/* - * @brief IsLegalIntervalStr - * Is interval legal? - * @param str - * @param numeric_only - * @return true legal - * @return false illegal - */ -static bool IsLegalIntervalStr(const char* str, bool numeric_only) -{ - size_t NBytes = (unsigned int)strlen(str); - if (NBytes > (MAX_CALENDAR_FIELD_LEN)) { - return false; - } - - /* numeric input recognize comma, space and minus sign */ - if (numeric_only) { - for (size_t i = 0; i < NBytes; i++) { - if (!isdigit(str[i]) && str[i] != ',' && str[i] != ' ' && str[i] != '-') { - return false; - } - } - return true; - } - - for (size_t i = 0; i < NBytes; i++) { - /* check whether the character is correct */ - if (IsIllegalIntervalCharacter(str[i])) { - return false; - } - } - return true; -} - - -/* - * @brief get_calendar_clause - * Get calendar clause and return its value; - * @param tokens calendar interval tokens - * @param clause clause name - * @return char* value - */ -static char *get_calendar_clause_val(char **tokens, const char *clause, bool numeric_only) -{ - char *val = NULL; - for (int i = 0; i < MAX_CALENDAR_FIELDS; i += 2) { - if (tokens[i] != NULL && pg_strcasecmp(tokens[i], clause) == 0) { - val = tokens[i + 1]; /* get clause's value */ - break; - } - } - if (val == NULL) { - return NULL; - } - - if (!IsLegalIntervalStr(val, numeric_only)) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Invalid value string for clause \'%s\'", clause), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } - return val; -} - -/* - * @brief get_calendar_freqency - * Get frequency_clause value. - * frequency_clause = "FREQ" "=" ( predefined_frequency | user_defined_frequency ) - * predefined_frequency = "YEARLY" | "MONTHLY" | "WEEKLY" | "DAILY" | "HOURLY" | "MINUTELY" | "SECONDLY" - * user_defined_frequency = named_schedule - * @param calendar - * @param tokens - * @return true clause exists - * @return false clause absent - */ -static bool get_calendar_freqency(Calendar calendar, char **tokens) -{ - char *val = get_calendar_clause_val(tokens, "freq", false); - if (val == NULL) { - return false; - } - - if (pg_strcasecmp(val, "yearly") == 0) { - calendar->frequency = YEARLY; - } else if (pg_strcasecmp(val, "monthly") == 0) { - calendar->frequency = MONTHLY; - } else if (pg_strcasecmp(val, "weekly") == 0) { - calendar->frequency = WEEKLY; - } else if (pg_strcasecmp(val, "daily") == 0) { - calendar->frequency = DAILY; - } else if (pg_strcasecmp(val, "hourly") == 0) { - calendar->frequency = HOURLY; - } else if (pg_strcasecmp(val, "minutely") == 0) { - calendar->frequency = MINUTELY; - } else if (pg_strcasecmp(val, "secondly") == 0) { - calendar->frequency = SECONDLY; - } else { - pfree_ext(tokens); - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Invalid frequency value \'%s\'.", val), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } - return true; -} - -/* - * @brief get_calendar_n_interval - * Get interval_clause. - * interval_clause = "INTERVAL" "=" intervalnum - * intervalnum = 1 through 99 - * @param calendar - * @param tokens - */ -static void get_calendar_n_interval(Calendar calendar, char **tokens) -{ - calendar->interval = 1; /* we ALWAYS set interval to 1 */ - char *val = get_calendar_clause_val(tokens, "interval", true); - if (val == NULL) { - return; - } - - int num = atoi(val); - if (num < 1 || num > MAX_CALENDAR_INTERVAL_NUM) { - pfree_ext(tokens); - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Interval \'%d\' not in range [1, 99].", num), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } - calendar->interval = num; -} - -static char *get_calendar_bymonth_val(Calendar calendar, char **tokens) -{ - char *val = get_calendar_clause_val(tokens, "bymonth", false); - if (val != NULL) { - /* apply bymonth rule if bymonth is specified */ - return val; - } - - if (calendar->frequency > MONTHLY) { - /* If we have higher frequency, every month is possible */ - for (int i = 0; i < MONTHS_PER_YEAR; i++) { - calendar->bymonth[i] = i + 1; - } - calendar->month_len = MONTHS_PER_YEAR; - calendar->date_depth *= calendar->month_len; - } else if (calendar->frequency < MONTHLY) { - /* If we have lower frquency, sync with start date value, fill it later */ - calendar->month_len = 0; - } else { - /* Frequency is MONTHLY, try optimize */ - int mod = (calendar->interval >= MONTHS_PER_YEAR) ? calendar->interval % MONTHS_PER_YEAR : calendar->interval; - if (mod == 0) { - mod = MONTHS_PER_YEAR; - } - /* We need the start month to figure out the ACTUAL month list, set it to negative and deal with it later */ - calendar->month_len = (MONTHS_PER_YEAR % mod == 0) ? MONTHS_PER_YEAR / mod : MONTHS_PER_YEAR; - calendar->date_depth *= calendar->month_len; - calendar->month_len *= -1; - calendar->bymonth[0] = mod; - } - return NULL; -} - -/* - * @brief get_calendar_bymonth - * Get bymonth_clause. - * bymonth_clause = "BYMONTH" "=" monthlist - * monthlist = month ( "," month)* - * month = numeric_month | char_month - * numeric_month = 1 | 2 | 3 ... 12 - * char_month = "JAN" | "FEB" | "MAR" | "APR" | "MAY" | "JUN" | "JUL" | "AUG" | "SEP" | "OCT" | "NOV" | "DEC" - * @param calendar - * @param tokens - */ -static void get_calendar_bymonth(Calendar calendar, char **tokens) -{ - const char *month_str[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}; - char *val = get_calendar_bymonth_val(calendar, tokens); - if (val == NULL) { - return; - } - - char *context = NULL; - char *tok = strtok_s(val, ",", &context); - bool used[MONTHS_PER_YEAR] = {0}; - while (tok != NULL) { - int month = 0; - if (isalpha(tok[0])) { - /* char in */ - for (int j = 0; j < MONTHS_PER_YEAR; j++) { - if (pg_strcasecmp(val, month_str[j]) == 0) { - month = j + 1; - break; - } - } - if (month == 0) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Invalid month token \'%s\'.", val), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } - } else { - /* numeric in */ - month = atoi(tok); - if (month < 1 || month > MONTHS_PER_YEAR) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Interval \'%d\' not in range [1, 12].", month), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } - } - tok = strtok_s(NULL, ",", &context); - - if (used[month - 1]) { - continue; - } - used[month - 1] = true; - } - for (int i = 0; i < MONTHS_PER_YEAR; i++) { - if (used[i]) { - calendar->bymonth[calendar->month_len] = i + 1; - calendar->month_len++; - } - } - calendar->date_depth *= (calendar->month_len == 0) ? 1 : calendar->month_len; - calendar->byfields |= INTERVAL_BYMONTH; -} - -/* - * @brief get_calendar_byweekno - * Get byweekno_clause. - * byweekno_clause = "BYWEEKNO" "=" weeknumber_list - * weeknumber_list = weeknumber ( "," weeknumber)* - * weeknumber = [minus] weekno - * weekno = 1 through 53 - * @param calendar - * @param tokens - */ -static void get_calendar_byweekno(Calendar calendar, char **tokens) -{ - char *val = get_calendar_clause_val(tokens, "byweekno", true); - if (val != NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("BYWEEKNO clause is currently unsupported."), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } -} - -/* - * @brief get_calendar_byyearday - * Get byyearday_clause. - * byyearday_clause = "BYYEARDAY" "=" yearday_list - * yearday_list = yearday ( "," yearday)* - * yearday = [minus] yeardaynum - * yeardaynum = 1 through 366 - * @param calendar - * @param tokens - */ -static void get_calendar_byyearday(Calendar calendar, char **tokens) -{ - char *val = get_calendar_clause_val(tokens, "byyearday", true); - if (val != NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("BYYEARDAY clause is currently unsupported."), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } -} - -/* - * @brief get_calendar_bydate - * Get bydate_clause. - * bydate_clause = "BYDATE" "=" date_list - * date_list = date ( "," date)* - * date = [YYYY]MMDD [ offset | span ] - * @param calendar - * @param tokens - */ -static void get_calendar_bydate(Calendar calendar, char **tokens) -{ - char *val = get_calendar_clause_val(tokens, "byweekno", true); - if (val != NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("BYDATE clause is currently unsupported."), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } -} - - -static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens) -{ - char *val = get_calendar_clause_val(tokens, "bymonthday", true); - if (val != NULL) { - /* apply bymonthday rule if bymonthday is specified */ - return val; - } - - if (calendar->frequency >= DAILY || calendar->frequency == WEEKLY) { - /* If we have higher frquency, every day is possible */ - for (int i = 0; i < DAYS_PER_MONTH + 1; i++) { - calendar->bymonthday[i] = i + 1; - } - calendar->monthday_len = DAYS_PER_MONTH + 1; - calendar->date_depth *= calendar->monthday_len; - } else { - /* If we have lower frquency, sync with start date value, fill it later */ - calendar->monthday_len = 0; - } - /* We cannot optimize any further since monthday/yearday are not perfectly periodic */ - return NULL; -} - - -/* - * @brief get_calendar_bymonth - * Get bymonthday_clause. - * bymonthday_clause = "BYMONTHDAY" "=" monthday_list - * monthday_list = monthday ( "," monthday)* - * monthday = [minus] monthdaynum - * monthdaynum = 1 through 31 - * @param calendar - * @param tokens - */ -static void get_calendar_bymonthday(Calendar calendar, char **tokens) -{ - char *val = get_calendar_bymonthday_val(calendar, tokens); - if (val == NULL) { - return; - } - - char *context = NULL; - char *tok = strtok_s(val, ",", &context); - bool used_p[DAYS_PER_MONTH + 1] = {0}; - bool used_n[DAYS_PER_MONTH + 1] = {0}; - while (tok != NULL) { - int monthday = atoi(tok); - if (monthday < -(DAYS_PER_MONTH + 1) || monthday > (DAYS_PER_MONTH + 1) || monthday == 0) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Invalid monthday \'%d\'.", monthday), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } - tok = strtok_s(NULL, ",", &context); - - /* considering both positive and negative parts */ - if (monthday > 0 && used_p[monthday - 1]) { - continue; - } - if (monthday < 0 && used_n[(-monthday) - 1]) { - continue; - } - - if (monthday > 0) { - used_p[monthday - 1] = true; - } - if (monthday < 0) { - used_n[(-monthday) - 1] = true; - } - } - /* there is no point of making two seperate loops for all monthdays, may refactor later. */ - for (int i = 0; i <= DAYS_PER_MONTH; i++) { - if (used_p[i]) { - calendar->bymonthday[calendar->monthday_len] = i + 1; - calendar->monthday_len++; - } - } - for (int i = DAYS_PER_MONTH; i >= 0; i--) { - if (used_n[i]) { - calendar->bymonthday[calendar->monthday_len] = -(i + 1); - calendar->monthday_len++; - } - } - calendar->date_depth *= (calendar->monthday_len == 0) ? 1 : calendar->monthday_len; - calendar->byfields |= INTERVAL_BYMONTHDAY; -} - -/* - * @brief get_calendar_byday - * Get byday_clause. - * byday_clause = "BYDAY" "=" byday_list - * byday_list = byday ( "," byday)* - * byday = [weekdaynum] day - * weekdaynum = [minus] daynum - * daynum = 1 through 53 -- yearly - * daynum = 1 through 5 -- monthly - * day = "MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN" - * @param calendar - * @param tokens - */ -static void get_calendar_byday(Calendar calendar, char **tokens) -{ - char *val = get_calendar_clause_val(tokens, "byday", true); - if (val != NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("BYDAY clause is currently unsupported."), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } -} - - -static char *get_calendar_byhour_val(Calendar calendar, char **tokens) -{ - char *val = get_calendar_clause_val(tokens, "byhour", true); - if (val != NULL) { - /* apply byhour rule if byhour is specified */ - return val; - } - - if (calendar->frequency > HOURLY) { - /* If we have higher frequency, every hour is possible */ - for (int i = 0; i < HOURS_PER_DAY; i++) { - calendar->byhour[i] = i; - } - calendar->hour_len = HOURS_PER_DAY; - calendar->time_depth *= calendar->hour_len; - } else if (calendar->frequency < HOURLY) { - /* If we have lower frequency, sync with start date value, fill it later */ - calendar->hour_len = 0; - } else { - /* frequency matched, try optimize */ - int mod = (calendar->interval >= HOURS_PER_DAY) ? calendar->interval % HOURS_PER_DAY : calendar->interval; - if (mod == 0) { - mod = HOURS_PER_DAY; - } - /* We need the start hour to figure out the ACTUAL hour list, set it to negative and deal with it later */ - calendar->hour_len = (HOURS_PER_DAY % mod == 0) ? HOURS_PER_DAY / mod : HOURS_PER_DAY; - calendar->time_depth *= calendar->hour_len; - calendar->hour_len *= -1; - calendar->byhour[0] = mod; - } - return NULL; -} - -/* - * @brief get_calendar_byhour - * Get byhour_clause. - * byhour_clause = "BYHOUR" "=" hour_list - * hour_list = hour ( "," hour)* - * hour = 0 through 23 - * @param calendar - * @param tokens - */ -static void get_calendar_byhour(Calendar calendar, char **tokens) -{ - char *val = get_calendar_byhour_val(calendar, tokens); - if (val == NULL) { - return; - } - - char *context = NULL; - char *tok = strtok_s(val, ",", &context); - bool used[HOURS_PER_DAY] = {0}; - while (tok != NULL) { - int hour = atoi(tok); - if (hour < 0 || hour >= HOURS_PER_DAY) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Invalid time \'%d\' o\' clock.", hour), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } - tok = strtok_s(NULL, ",", &context); - - if (used[hour]) { - continue; - } - used[hour] = true; - } - for (int i = 0; i < HOURS_PER_DAY; i++) { - if (used[i]) { - calendar->byhour[calendar->hour_len] = i; - calendar->hour_len++; - } - } - calendar->time_depth *= (calendar->hour_len == 0) ? 1 : calendar->hour_len; - calendar->byfields |= INTERVAL_BYHOUR; -} - - -static char *get_calendar_byminute_val(Calendar calendar, char **tokens) -{ - char *val = get_calendar_clause_val(tokens, "byminute", true); - if (val != NULL) { - /* apply byminute rule if byminute is specified */ - return val; - } - - if (calendar->frequency > MINUTELY) { - /* If we have higher frequency, every minute is possible */ - for (int i = 0; i < MINS_PER_HOUR; i++) { - calendar->byminute[i] = i; - } - calendar->minute_len = MINS_PER_HOUR; - calendar->time_depth *= calendar->minute_len; - } else if (calendar->frequency < MINUTELY) { - /* If we have lower frequency, sync with start date value, fill it later */ - calendar->minute_len = 0; - } else { - /* frequency matched, try optimize */ - int mod = (calendar->interval >= MINS_PER_HOUR) ? calendar->interval % MINS_PER_HOUR : calendar->interval; - if (mod == 0) { - mod = MINS_PER_HOUR; - } - /* We need the start minute to figure out the ACTUAL minute list, set it to negative and deal with it later */ - calendar->minute_len = (MINS_PER_HOUR % mod == 0) ? MINS_PER_HOUR / mod : MINS_PER_HOUR; - calendar->time_depth *= calendar->minute_len; - calendar->minute_len *= -1; - calendar->byminute[0] = mod; - } - return NULL; -} - -/* - * @brief get_calendar_byminute - * Get byminute_clause. - * byminute_clause = "BYMINUTE" "=" minute_list - * minute_list = minute ( "," minute)* - * minute = 0 through 59 - * @param calendar - * @param tokens - */ -static void get_calendar_byminute(Calendar calendar, char **tokens) -{ - char *val = get_calendar_byminute_val(calendar, tokens); - if (val == NULL) { - return; - } - - char *context = NULL; - char *tok = strtok_s(val, ",", &context); - bool used[MINS_PER_HOUR] = {0}; - while (tok != NULL) { - int minute = atoi(tok); - if (minute < 0 || minute >= MINS_PER_HOUR) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Invalid time \'%d\' minute [0, 59].", minute), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } - tok = strtok_s(NULL, ",", &context); - - if (used[minute]) { - continue; - } - used[minute] = true; - } - for (int i = 0; i < MINS_PER_HOUR; i++) { - if (used[i]) { - calendar->byminute[calendar->minute_len] = i; - calendar->minute_len++; - } - } - calendar->time_depth *= (calendar->minute_len == 0) ? 1 : calendar->minute_len; - calendar->byfields |= INTERVAL_BYMINUTE; -} - -static char *get_calendar_bysecond_val(Calendar calendar, char **tokens) -{ - char *val = get_calendar_clause_val(tokens, "bysecond", true); - if (val != NULL) { - /* apply bysecond rule if bysecond is specified */ - return val; - } - - Assert(calendar->frequency <= SECONDLY); - /* Even higher frequency is unavailable */ - if (calendar->frequency < SECONDLY) { - /* If we have lower frequency, sync with start date value, fill it later */ - calendar->second_len = 0; - } else { - /* frequency matched, try optimize */ - int mod = (calendar->interval >= SECS_PER_MINUTE) ? calendar->interval % SECS_PER_MINUTE : calendar->interval; - if (mod == 0) { - mod = SECS_PER_MINUTE; - } - /* We need the start second to figure out the ACTUAL second list, set it to negative and deal with it later */ - calendar->second_len = (SECS_PER_MINUTE % mod == 0) ? SECS_PER_MINUTE / mod : SECS_PER_MINUTE; - calendar->time_depth *= calendar->second_len; - calendar->second_len *= -1; - calendar->bysecond[0] = mod; - } - return NULL; -} - -/* - * @brief get_calendar_bysecond - * Get bysecond_clause. - * bysecond_clause = "BYSECOND" "=" second_list - * second_list = second ( "," second)* - * second = 0 through 59 - * @param interval - * @param tokens - */ -static void get_calendar_bysecond(Calendar calendar, char **tokens) -{ - char *val = get_calendar_bysecond_val(calendar, tokens); - if (val == NULL) { - return; - } - - char *context = NULL; - char *tok = strtok_s(val, ",", &context); - bool used[SECS_PER_MINUTE] = {0}; - while (tok != NULL) { - int second = atoi(tok); - if (second < 0 || second >= SECS_PER_MINUTE) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Invalid time \'%d\' seconds [0, 59].", second), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } - tok = strtok_s(NULL, ",", &context); - - if (used[second]) { - continue; - } - used[second] = true; - } - for (int i = 0; i < SECS_PER_MINUTE; i++) { - if (used[i]) { - calendar->bysecond[calendar->second_len] = i; - calendar->second_len++; - } - } - calendar->time_depth *= (calendar->second_len == 0) ? 1 : calendar->second_len; - calendar->byfields |= INTERVAL_BYSECOND; -} - - -/* - * @brief tokenize_str - * Tokenize string with given delimiters. - * @param src source string - * @param delims delimiters - * @param fields number of tokens needed - * @return char** an array of tokens generated - */ -static char **tokenize_str(char *src, const char *delims, int fields) -{ - char **tokens = (char **)palloc0(sizeof(char *) * fields); - int count = 0; - char *context = NULL; - char *tok = strtok_s(src, delims, &context); - while (tok != NULL) { - tokens[count] = tok; - tok = strtok_s(NULL, delims, &context); - count++; - if (count >= fields) { - pfree_ext(tokens); - return NULL; - } - } - return tokens; -} - -static int validate_field_names(char **toks) -{ - bool valid = false; - const int name_pos_step = 2; - const char *supported_fields[SUPPORTED_FIELDS] = {"freq", "interval", "bymonth", "bymonthday", "byhour", - "byminute", "bysecond"}; - bool fields_used[SUPPORTED_FIELDS] = {0}; - for (int i = 0; i < MAX_CALENDAR_FIELDS; i += name_pos_step) { - for (int j = 0; j < SUPPORTED_FIELDS; j++) { - if (toks[i] == NULL) { - /* This is rare, usually token is not empty in the middle */ - valid = true; - break; - } - if (pg_strcasecmp(toks[i], supported_fields[j]) != 0) { - continue; - } - if (fields_used[j]) { - /* duplicate field name */ - valid = false; - } else { - fields_used[j] = true; - valid = true; - } - break; /* here is way pass guarding condition, break it */ - } - if (!valid) { - return i; - } - valid = false; - } - return -1; -} - -/* - * @brief interpret_calendar_interval - * The main interpreter of calendar interval. - * @param interval_str - * @return Calendar - */ -Calendar interpret_calendar_interval(char *calendar_str) -{ - Assert(calendar_str != NULL); - bool field = false; - - /* Make token lists */ - char **str_toks = tokenize_str(calendar_str, " =;", MAX_CALENDAR_FIELDS); - if (str_toks == NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Unable to parse calendaring string."), - errcause("Calendaring string is too long/invalid"), - erraction("Please modify the calendaring string."))); - } - - int pos = validate_field_names(str_toks); - if (pos >= 0) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("Fail to evaluate calendaring string."), - errdetail("Incorrect/duplicate clause name '%s'.", str_toks[pos]), errcause("N/A"), - erraction("Please modify the calendaring string."))); - } - - /* Make Calendar */ - Calendar calendar = (Calendar)palloc0(sizeof(CalendarContext)); - - /* Main interpreter */ - field = get_calendar_freqency(calendar, str_toks); - if (!field || pg_strcasecmp(str_toks[0], "freq") != 0) { - /* - * Return NULL if frequency clause is missing(or not the first), - * possibly not a calendaring syntax - */ - pfree_ext(calendar); - return NULL; - } - get_calendar_n_interval(calendar, str_toks); - - /* by*** clauses */ - calendar->date_depth = 1; - calendar->time_depth = 1; - get_calendar_bymonth(calendar, str_toks); - get_calendar_byweekno(calendar, str_toks); - get_calendar_byyearday(calendar, str_toks); - get_calendar_bydate(calendar, str_toks); - get_calendar_bymonthday(calendar, str_toks); - get_calendar_byday(calendar, str_toks); - get_calendar_byhour(calendar, str_toks); - get_calendar_byminute(calendar, str_toks); - get_calendar_bysecond(calendar, str_toks); - - pfree_ext(str_toks); - return calendar; -} - -/* - * @brief get_calendar_period - * Get the calendar period in the form of Interval. - * @param calendar - * @param num_of_period - * @return Interval* - */ -static Interval *get_calendar_period(Calendar calendar, int num_of_period) -{ - const char *freq_str = NULL; - if (calendar->frequency == YEARLY) { - freq_str = "years"; - } else if (calendar->frequency == MONTHLY) { - freq_str = "months"; - } else if (calendar->frequency == WEEKLY) { - freq_str = "weeks"; - } else if (calendar->frequency == DAILY) { - freq_str = "days"; - } else if (calendar->frequency == HOURLY) { - freq_str = "hours"; - } else if (calendar->frequency == MINUTELY) { - freq_str = "minutes"; - } else if (calendar->frequency == SECONDLY) { - freq_str = "seconds"; - } else { - /* rely on upper level checks, which will eventually reach the MAX_CALENDAR_DEPTH and stop */ - return NULL; - } - - return calendar_construct_interval(freq_str, calendar->interval * num_of_period); -} - -/* - * @brief copy_calendar_dates - * copy batches of timestamps. - * @param dates target timestamp array - * @param nvals number of batches - * @param cnt number of existing values - * @param date_in optional, only valid when dates is empty - */ -static void copy_calendar_dates(TimestampTz *timeline, int nvals, int cnt) -{ - /* copy nvals number of existing timestamps */ - if (nvals <= 1) { - return; - } - for (int i = 0; i < cnt; i++) { - for (int j = 1; j < nvals; j++) { - timeline[i + (j * cnt)] = timeline[i]; - } - } -} - -/* - * @brief validate_calendar_monthday - * Check if a given monthday is valid. - * @param year - * @param month - * @param mday - * @return true - * @return false - */ -static bool validate_calendar_monthday(int year, int month, int mday) -{ - bool month_31[MONTHS_PER_YEAR] = {1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1}; - if ((!month_31[month - 1] && mday > 30) || (month_31[month - 1] && mday > 31)) { - return false; - } - if ((!isleap(year) && month == 2 && mday > 29) || (isleap(year) && month == 2 && mday > 28)) { - return false; - } - /* monthday can be negative */ - if (!isleap(year) && month == 2 && mday < -28) { - return false; - } - if (isleap(year) && month == 2 && mday < -29) { - return false; - } - if (!month_31[month - 1] && mday < -30) { - return false; - } - return true; -} - -/* - * @brief evaluate_calendar_bymonth - * Evaluate bymonth field. - * @param calendar - * @param dates - * @param cnt - */ -static void evaluate_calendar_bymonth(Calendar calendar, TimestampTz *timeline, int *cnt) -{ - Assert(*cnt <= calendar->date_depth); - if (*cnt == 0) { - return; - } - - Interval *interval = NULL; - int chunk = *cnt; - TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("month"), calendar->ref_date); - - if (calendar->month_len == 0) { - calendar->bymonth[0] = calendar->tm.tm_mon; - calendar->month_len = 1; - } else if (calendar->month_len < 0) { - int mod = calendar->bymonth[0]; - int start = (calendar->tm.tm_mon - 1) - (((calendar->tm.tm_mon - 1) / mod) * mod) + 1; - calendar->month_len = 0; - while (start < MONTHS_PER_YEAR) { - calendar->bymonth[calendar->month_len] = start; - start += mod; - calendar->month_len++; - } - } - - *cnt = 0; - copy_calendar_dates(timeline, calendar->month_len, chunk); - for (int i = 0; i < calendar->month_len; i++) { - interval = calendar_construct_interval("months", calendar->bymonth[i] - 1); - for (int j = i * chunk; j < (i + 1) * chunk; j++) { - timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); - if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { - (*cnt)++; - } - } - pfree_ext(interval); - } -} - -static void evaluate_calendar_byweekno(Calendar calendar, TimestampTz *timeline, int *cnt) -{ - return; -} - -static void evaluate_calendar_byyearday(Calendar calendar, TimestampTz *timeline, int *cnt) -{ - return; -} - -/* - * @brief evaluate_calendar_bymonthday - * Evaluate bymonthday field. - * @param calendar - * @param timeline - * @param cnt - */ -static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timeline, int *cnt) -{ - Assert(*cnt <= calendar->date_depth); - if (*cnt == 0) { - return; - } - - int chunk = *cnt; - Interval *interval = NULL; - TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("day"), calendar->ref_date); - - if (calendar->monthday_len == 0) { - calendar->bymonthday[0] = calendar->tm.tm_mday; - calendar->monthday_len = 1; - } - - *cnt = 0; - int tz = 0; - fsec_t fsec; - struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */ - copy_calendar_dates(timeline, calendar->monthday_len, chunk); - for (int i = 0; i < calendar->monthday_len; i++) { - if (calendar->bymonthday[i] < 0) { - interval = calendar_construct_interval("months", 1); - } else { - interval = calendar_construct_interval("days", calendar->bymonthday[i] - 1); - } - for (int j = i * chunk; j < (i + 1) * chunk; j++) { - if (timestamp2tm(timeline[j], &tz, tm, &fsec, NULL, NULL) != 0) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), errdetail("Invalid timeline."), - errcause("N/A"), erraction("Please modify the calendaring string."))); - } - if (!validate_calendar_monthday(tm->tm_year, tm->tm_mon, calendar->bymonthday[i])) { - /* skip if month day is invalid */ - continue; - } - timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); - if (calendar->bymonthday[i] <= 0) { - pfree_ext(interval); - interval = calendar_construct_interval("days", -(calendar->bymonthday[i])); - timeline[*cnt] = DatumGetTimestampTz(timestamp_mi_interval(timeline[j], interval)); - } - if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { - (*cnt)++; - } - } - pfree_ext(interval); - } -} - -/* - * @brief evaluate_calendar_byhour - * Evaluate byhour field. - * @param calendar - * @param timeline - * @param cnt - */ -static void evaluate_calendar_byhour(Calendar calendar, TimestampTz *timeline, int *cnt) -{ - Assert(*cnt <= calendar->time_depth); - if (*cnt == 0) { - return; - } - - Interval *interval = NULL; - int chunk = *cnt; - TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("hour"), calendar->ref_date); - - if (calendar->hour_len == 0) { - calendar->byhour[0] = calendar->tm.tm_hour; - calendar->hour_len = 1; - } else if (calendar->hour_len < 0) { - int mod = calendar->byhour[0]; - int start = calendar->tm.tm_hour - ((calendar->tm.tm_hour / mod) * mod); - calendar->hour_len = 0; - while (start < HOURS_PER_DAY) { - calendar->byhour[calendar->hour_len] = start; - start += mod; - calendar->hour_len++; - } - } - - *cnt = 0; - copy_calendar_dates(timeline, calendar->hour_len, chunk); - for (int i = 0; i < calendar->hour_len; i++) { - interval = calendar_construct_interval("hours", calendar->byhour[i]); - for (int j = i * chunk; j < (i + 1) * chunk; j++) { - timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); - if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { - (*cnt)++; - } - } - pfree_ext(interval); - } -} - -/* - * @brief evaluate_calendar_byminute - * Evaluate byminunte field. - * @param calendar - * @param timeline - * @param cnt - */ -static void evaluate_calendar_byminute(Calendar calendar, TimestampTz *timeline, int *cnt) -{ - Assert(*cnt <= calendar->time_depth); - if (*cnt == 0) { - return; - } - - Interval *interval = NULL; - int chunk = *cnt; - TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("minute"), calendar->ref_date); - - if (calendar->minute_len == 0) { - calendar->byminute[0] = calendar->tm.tm_min; - calendar->minute_len = 1; - } else if (calendar->minute_len < 0) { - int mod = calendar->byminute[0]; - int start = calendar->tm.tm_min - ((calendar->tm.tm_min / mod) * mod); - calendar->minute_len = 0; - while (start < MINS_PER_HOUR) { - calendar->byminute[calendar->minute_len] = start; - start += mod; - calendar->minute_len++; - } - } - - *cnt = 0; - copy_calendar_dates(timeline, calendar->minute_len, chunk); - for (int i = 0; i < calendar->minute_len; i++) { - interval = calendar_construct_interval("minutes", calendar->byminute[i]); - for (int j = i * chunk; j < (i + 1) * chunk; j++) { - timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); - if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { - (*cnt)++; - } - } - pfree_ext(interval); - } -} - -/* - * @brief evaluate_calendar_bysecond - * Evaluate bysecond field. - * @param calendar - * @param timeline - * @param cnt - */ -static void evaluate_calendar_bysecond(Calendar calendar, TimestampTz *timeline, int *cnt) -{ - Assert(*cnt <= calendar->time_depth); - if (*cnt == 0) { - return; - } - - Interval *interval = NULL; - int chunk = *cnt; - TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("second"), calendar->ref_date); - - if (calendar->second_len == 0) { - calendar->bysecond[0] = calendar->tm.tm_sec; - calendar->second_len = 1; - } else if (calendar->second_len < 0) { - int mod = calendar->bysecond[0]; - int start = calendar->tm.tm_sec - ((calendar->tm.tm_sec / mod) * mod); - calendar->second_len = 0; - while (start < SECS_PER_MINUTE) { - calendar->bysecond[calendar->second_len] = start; - start += mod; - calendar->second_len++; - } - } - - *cnt = 0; - copy_calendar_dates(timeline, calendar->second_len, chunk); - for (int i = 0; i < calendar->second_len; i++) { - interval = calendar_construct_interval("seconds", calendar->bysecond[i]); - for (int j = i * chunk; j < (i + 1) * chunk; j++) { - timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); - if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { - (*cnt)++; - } - } - pfree_ext(interval); - } -} - -/* - * @brief fastforward_calendar_period - * Fastforward to the date right before the date after to save some computing power. - * @param calendar - * @param start_date - * @param date_after - */ -static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_date, TimestampTz date_after) -{ - Interval *interval = calendar_construct_interval("second", 1); - if (timestamptz_cmp_internal(*start_date, date_after) >= 0) { - /* No rewind, no equal */ - calendar->ref_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, interval)); - pfree_ext(interval); - return; - } - - TimestampTz new_start_date; - Interval *period = get_calendar_period(calendar); - if (period == NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), errdetail("Broken interval clause."), - errcause("N/A"), erraction("Please modify the calendaring string."))); - } - Datum pace_datum = DirectFunctionCall2(interval_part, CStringGetTextDatum("epoch"), - PointerGetDatum(period)); - int pace = (int)DatumGetFloat8(pace_datum); - pfree_ext(period); - - long elapsed_sec = 0; - int elapsed_usec = 0; - TimestampDifference(*start_date, date_after, &elapsed_sec, &elapsed_usec); - - /* fastforward span in seconds = floor(elapsed / pace) * pace */ - int num_of_periods = (elapsed_sec / pace); - if (num_of_periods >= 1) { - Interval *ff_span = get_calendar_period(calendar, num_of_periods); - if (ff_span == NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), errdetail("Broken interval clause."), - errcause("N/A"), erraction("Please modify the calendaring string."))); - } - new_start_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, ff_span)); - pfree_ext(ff_span); - *start_date = new_start_date; - } - calendar->ref_date = DatumGetTimestampTz(timestamp_pl_interval(date_after, interval)); - pfree_ext(interval); -} - -/* - * @brief recheck_calendar_period - * Usually we need to double check if a given timestamp resonate with original frequency. - * @param calendar - * @param start_date - * @param next_date - * @return true resonate - * @return false does not resonate - */ -static bool recheck_calendar_period(Calendar calendar, TimestampTz start_date, TimestampTz next_date) -{ - const char *field_str = NULL; - int multiplier = 1; - if (calendar->frequency == YEARLY) { - field_str = "year"; - } else if (calendar->frequency == MONTHLY) { - field_str = "month"; - } else if (calendar->frequency == WEEKLY) { - field_str = "day"; - multiplier = DAYS_PER_WEEK; - } else if (calendar->frequency == DAILY) { - field_str = "day"; - } else if (calendar->frequency == HOURLY) { - field_str = "hour"; - } else if (calendar->frequency == MINUTELY) { - field_str = "minute"; - } else if (calendar->frequency == SECONDLY) { - field_str = "second"; - } else { - /* rely on upper level checks, which will eventually reach the MAX_CALENDAR_DEPTH and stop */ - return false; - } - - start_date = truncate_calendar_date(CStringGetTextDatum(field_str), start_date); - next_date = truncate_calendar_date(CStringGetTextDatum(field_str), next_date); - - int64 elapsed = timestamp_diff_internal(cstring_to_text(field_str), start_date, next_date, true); - if (elapsed % (calendar->interval * multiplier) == 0) { - return true; - } - return false; -} - -/* - * @brief timestamp_cmp_func - * Compare func for qsort - * @param dt1 - * @param dt2 - * @return int - */ -static int timestamp_cmp_func(const void *dt1, const void *dt2) -{ - TimestampTz *t1 = (TimestampTz *)dt1; - TimestampTz *t2 = (TimestampTz *)dt2; - - return timestamp_cmp_internal(*t1, *t2); -} - -/* - * @brief find_nearest_calendar_time - * Find the nearest timestamp from all valid timestamps. - * @param calendar context - * @param timeline timestamp list - * @param start start time - * @param cnt number of valid final timestamps - * @param nearest out value - * @return true found - * @return false not found - */ -static bool find_nearest_calendar_time(Calendar calendar, TimestampTz *timeline, TimestampTz start, int cnt, - TimestampTz *nearest) -{ - /* sort all timestamp */ - qsort(timeline, cnt, sizeof(TimestampTz), timestamp_cmp_func); - - for (int i = 0; i < cnt; i++) { - /* Must greater than ref date */ - if (timestamp_cmp_internal(timeline[i], calendar->ref_date) >= 0 && - recheck_calendar_period(calendar, start, timeline[i])) { - *nearest = timeline[i]; - return true; - } - } - return false; -} - -/* - * @brief evaluate_calendar_period - * - * @param calendar - * @param period - * @param start_date - * @param next_date - * @return true success - * @return false fail - */ -static bool evaluate_calendar_period(Calendar calendar, TimestampTz *timeline, TimestampTz *sub_timeline, - TimestampTz start_date, TimestampTz *next_date) -{ - int date_cnt = 1; - evaluate_calendar_bymonth(calendar, timeline, &date_cnt); - evaluate_calendar_byweekno(calendar, timeline, &date_cnt); - evaluate_calendar_byyearday(calendar, timeline, &date_cnt); - evaluate_calendar_bymonthday(calendar, timeline, &date_cnt); - - /* sort the timeline so that we can break immediately once we find any timestamps later */ - qsort(timeline, date_cnt, sizeof(TimestampTz), timestamp_cmp_func); - - /* - * Use the sorted timeline (date part) to generate matching timestamp for each day. - */ - int time_cnt; - for (int i = 0; i < date_cnt; i++) { - time_cnt = 1; - sub_timeline[0] = timeline[i]; /* starting date */ - evaluate_calendar_byhour(calendar, sub_timeline, &time_cnt); - evaluate_calendar_byminute(calendar, sub_timeline, &time_cnt); - evaluate_calendar_bysecond(calendar, sub_timeline, &time_cnt); - if (find_nearest_calendar_time(calendar, sub_timeline, start_date, time_cnt, next_date)) { - return true; - } - } - return false; -} - -/* - * @brief prepare_calendar_period - * - * @param calendar - * @param base_date - * @param timeline - */ -static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, TimestampTz *timeline) -{ - Assert(calendar != NULL); - Assert(calendar->frequency <= MAX_FREQ); - Assert(calendar->frequency >= YEARLY); - Assert(calendar->date_depth > 0); - Assert(calendar->time_depth > 0); - timeline[0] = truncate_calendar_date(CStringGetTextDatum("year"), TimestampTzGetDatum(base_date)); - - if (calendar->date_depth > MAX_CALENDAR_DATE_DEPTH || calendar->time_depth > SECS_PER_DAY) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), - errdetail("The scheduler run out of attempts to find a valid date in the foreesable future."), - errcause("N/A"), erraction("Please modify the calendaring string."))); - } - - fsec_t fsec; - struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */ - int tz; - if (timestamp2tm(base_date, &tz, tm, &fsec, NULL, NULL) != 0) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), errdetail("Fail to truncate start date."), - errcause("N/A"), erraction("Please modify the calendaring string."))); - } - calendar->tm = tt; - calendar->fsec = fsec; - - if (timestamp_cmp_internal(timeline[0], calendar->ref_date) > 0) { - calendar->ref_date = timeline[0]; - } -} - -/* - * @brief get_next_calendar_period - * Get the next calendar period. Usually a year, but can be multiple years if interval is large. - * @param calendar - * @param base_date - * @return TimestampTz - */ -static TimestampTz get_next_calendar_period(Calendar calendar, TimestampTz base_date) -{ - int span_multiplier = 1; - if (calendar->frequency == MONTHLY && calendar->interval > MONTHS_PER_YEAR) { - span_multiplier = calendar->interval / MONTHS_PER_YEAR; - } - Interval *span = calendar_construct_interval("years", span_multiplier); - base_date = DatumGetTimestampTz(timestamp_pl_interval(base_date, span)); - pfree_ext(span); - return base_date; -} - - -/* - * @brief evaluate_calendar_interval - * Calculate next date base on start date. - * @param calendar - * @param start_date - * @return char* - */ -static TimestampTz evaluate_calendar_interval(Calendar calendar, TimestampTz start_date) -{ - if (calendar == NULL) { - return start_date; - } - - int try_count = 0; - TimestampTz next_date = start_date; - TimestampTz base_date = start_date; /* base date of each period, update with loop */ - TimestampTz *timeline = (TimestampTz *)palloc0(calendar->date_depth * sizeof(TimestampTz)); - TimestampTz *sub_timeline = (TimestampTz *)palloc0((calendar->time_depth + 1) * sizeof(TimestampTz)); - while (try_count < YEARS_PER_CENTURY) { - prepare_calendar_period(calendar, base_date, timeline); - if (evaluate_calendar_period(calendar, timeline, sub_timeline, start_date, &next_date)) { - break; - } - base_date = get_next_calendar_period(calendar, base_date); - CHECK_FOR_INTERRUPTS(); - try_count++; - } - - pfree_ext(timeline); - pfree_ext(sub_timeline); - if (try_count >= YEARS_PER_CENTURY) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("Cannot evaluate calendar clause."), errdetail("Calender clause too deep."), - errcause("N/A"), erraction("Please modify the calendaring string."))); - } - return next_date; -} - -/* - * @brief evaluate_repeat_interval - * Calculate next date base on start date and calendar string. - * @param calendar_in - * @param base_time - * @return Datum - */ -Datum evaluate_repeat_interval(Datum calendar_in, Datum start_date, Datum date_after) -{ - char *calendar_str = text_to_cstring(DatumGetTextP(calendar_in)); - if (strlen(calendar_str) > (size_t)MAX_CALENDAR_STR_LEN) { - pfree_ext(calendar_str); - return calendar_in; - } - - TimestampTz start_date_raw = DatumGetTimestampTz(start_date); - Calendar calendar = interpret_calendar_interval(calendar_str); - - /* remove fsec part */ - start_date_raw = truncate_calendar_date(CStringGetTextDatum("second"), TimestampTzGetDatum(start_date_raw)); - - /* fastforward start date if date_after is specified */ - if (calendar != NULL) { - /* remove fsec part */ - date_after = truncate_calendar_date(CStringGetTextDatum("second"), TimestampTzGetDatum(date_after)); - fastforward_calendar_period(calendar, &start_date_raw, DatumGetTimestampTz(date_after)); - } - TimestampTz next_date = evaluate_calendar_interval(calendar, start_date_raw); - - pfree_ext(calendar_str); - if (calendar == NULL) { - /* return if not a calendar interval expression, `calendar` is already freed */ - return calendar_in; - } else { - pfree_ext(calendar); - } - return TimestampTzGetDatum(next_date); -} - -/* - * @brief evaluate_calendar_string_internal - * eval_calendar_string interface. - * @return Datum - */ -Datum evaluate_calendar_string_internal(PG_FUNCTION_ARGS) -{ - Datum string = PG_GETARG_DATUM(0); /* calendar string */ - Datum start_date = PG_GETARG_DATUM(1); /* start date */ - Datum date_after = PG_GETARG_DATUM(2); /* return date after */ - Datum new_next_date = evaluate_repeat_interval(string, start_date, date_after); - PG_RETURN_DATUM(new_next_date); -} \ No newline at end of file -- 2.34.1 From bb95d131f3feb0bc8c83a58b2981fd5540e40938 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:14:35 +0800 Subject: [PATCH 46/56] ADD file via upload --- .../process/job/gs_job_calendar.cpp | 1776 +++++++++++++++++ 1 file changed, 1776 insertions(+) create mode 100644 src/gausskernel/process/job/gs_job_calendar.cpp diff --git a/src/gausskernel/process/job/gs_job_calendar.cpp b/src/gausskernel/process/job/gs_job_calendar.cpp new file mode 100644 index 000000000..abd00eb3a --- /dev/null +++ b/src/gausskernel/process/job/gs_job_calendar.cpp @@ -0,0 +1,1776 @@ +/* + * 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. + * ------------------------------------------------------------------------- + * + * gs_job_calendar.cpp + * Calendaring syntax for dbe jobs. + * + * IDENTIFICATION + * src/gausskernel/process/job/gs_job_calendar.cpp + * + * ------------------------------------------------------------------------- + */ + + #include "postgres.h" + #include "miscadmin.h" + #include "utils/builtins.h" + #include "utils/dbe_scheduler.h" + +/* + * repeat_interval = frequency_clause + * [; interval=?] [; bymonth=?] [; byweekno=?] + * [; byyearday=?] [; bymonthday=?] [; byday=?] + * [; byhour=?] [; byminute=?] [; bysecond=?] + * + * frequency_clause = "FREQ" "=" frequency + * frequency = "YEARLY" | "MONTHLY" | "WEEKLY" | "DAILY" | + * "HOURLY" | "MINUTELY" | "SECONDLY" + * + * Note: + * POSIX time (tm) used in this section has following flags: + * int tm_sec Seconds [0,60]. (with leap second!!) + * int tm_min Minutes [0,59]. + * int tm_hour Hour [0,23]. + * int tm_mday Day of month [1,31]. + * int tm_mon Month of year [0,11]. (not ISO standard!! [1,12]) + * int tm_year Years since 1900. + * int tm_wday Day of week [0,6] (Sunday =0). (not ISO standard!! sun =7) + * int tm_yday Day of year [0,365]. (not ISO standard!! [1,366]) + * int tm_isdst Daylight Savings flag. + * + */ + +/* Initialize calendaring fields */ +static bool IsLegalIntervalStr(const char* str, bool numeric_only = false); +static char *get_calendar_clause_val(char **tokens, const char *clause, bool numeric_only = false); +static char **tokenize_str(char *src, const char *delims, int fields); +static int validate_field_names(char **toks); + +/* + * Interpreter functions + * Interpret calendaring syntax. + */ +static bool get_calendar_freqency(Calendar calendar, char **tokens); +static void get_calendar_n_interval(Calendar calendar, char **tokens); +static char *get_calendar_bymonth_val(Calendar calendar, char **tokens); +static void get_calendar_bymonth(Calendar calendar, char **tokens); +static void get_calendar_byweekno(Calendar calendar, char **tokens); /* unsupported */ +static void get_calendar_byyearday(Calendar calendar, char **tokens); /* unsupported */ +static void get_calendar_bydate(Calendar calendar, char **tokens); /* unsupported */ +static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens); +static void get_calendar_bymonthday(Calendar calendar, char **tokens); +static void get_calendar_byday(Calendar calendar, char **tokens); /* unsupported */ +static char *get_calendar_byhour_val(Calendar calendar, char **tokens); +static void get_calendar_byhour(Calendar calendar, char **tokens); +static char *get_calendar_byminute_val(Calendar calendar, char **tokens); +static void get_calendar_byminute(Calendar calendar, char **tokens); +static char *get_calendar_bysecond_val(Calendar calendar, char **tokens); +static void get_calendar_bysecond(Calendar calendar, char **tokens); +Calendar interpret_calendar_interval(char *calendar_str); /* interpreter main */ + +/* + * Evaluation functions + * Calculate calendaring interval. + */ +static Interval *get_calendar_period(Calendar calendar, int num_of_period = 1); +static void copy_calendar_dates(TimestampTz *timeline, int nvals, int cnt); +static bool validate_calendar_monthday(int year, int month, int mday); +static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_date, TimestampTz date_after); +static bool recheck_calendar_period(Calendar calendar, TimestampTz start_date, TimestampTz next_date); +static int timestamp_cmp_func(const void *dt1, const void *dt2); +static TimestampTz get_next_calendar_period(Calendar calendar, TimestampTz base_date); +static bool find_nearest_calendar_time(Calendar calendar, TimestampTz *timeline, TimestampTz start, int cnt, + TimestampTz *nearest); + +/* Calendaring Interval Calculator */ +static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, TimestampTz *timeline); +static void evaluate_calendar_bymonth(Calendar calendar, TimestampTz *timeline, int *cnt); +static void evaluate_calendar_byweekno(Calendar calendar, TimestampTz *timeline, int *cnt); /* unsupported */ +static void evaluate_calendar_byyearday(Calendar calendar, TimestampTz *timeline, int *cnt); /* unsupported */ +static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timeline, int *cnt); +static void evaluate_calendar_byhour(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */ +static void evaluate_calendar_byminute(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */ +static void evaluate_calendar_bysecond(Calendar calendar, TimestampTz *timeline, int *cnt); /* sub_timeline */ +static bool evaluate_calendar_period(Calendar calendar, TimestampTz *timeline, TimestampTz *sub_timeline, + TimestampTz start_date, TimestampTz *next_date); +static TimestampTz evaluate_calendar_interval(Calendar calendar, TimestampTz start_date); /* evaluate main */ + +/* + * @brief IsLegalIntervalStr + * Is interval legal? + * @param str + * @param numeric_only + * @return true legal + * @return false illegal + *//* + * 函数名:IsLegalIntervalStr + * 功能:检查给定字符串是否是合法的时间间隔字符串。 + * 参数: + * - str:要检查的字符串。 + * - numeric_only:是否只允许数字输入。 + * 返回值: + * - 布尔类型,如果字符串是合法的则返回true,否则返回false。 + */ + +static bool IsLegalIntervalStr(const char* str, bool numeric_only) +{ + size_t NBytes = (unsigned int)strlen(str); + + // 如果字符串长度超过最大允许长度,则认为不合法 + if (NBytes > (MAX_CALENDAR_FIELD_LEN)) { + return false; + } + + /* 对于只允许数字输入的情况,认可逗号、空格和减号 */ + if (numeric_only) { + for (size_t i = 0; i < NBytes; i++) { + // 如果字符既不是数字也不是逗号、空格或减号,则认为不合法 + if (!isdigit(str[i]) && str[i] != ',' && str[i] != ' ' && str[i] != '-') { + return false; + } + } + return true; + } + + for (size_t i = 0; i < NBytes; i++) { + /* 检查字符是否正确 */ + if (IsIllegalIntervalCharacter(str[i])) { + return false; + } + } + return true; +} + +/* + * 函数名:get_calendar_clause_val + * 功能:从tokens数组中获取与clause对应的值。 + * 参数: + * - tokens:存储了键值对的字符串数组。 + * - clause:要获取值的键。 + * - numeric_only:是否只允许数字输入。 + * 返回值: + * - char类型指针,如果找到与clause对应的值,则返回该值,否则返回NULL。 + */ +static char *get_calendar_clause_val(char **tokens, const char *clause, bool numeric_only) +{ + char *val = NULL; + for (int i = 0; i < MAX_CALENDAR_FIELDS; i += 2) { + // 如果找到与clause对应的键,则获取对应的值 + if (tokens[i] != NULL && pg_strcasecmp(tokens[i], clause) == 0) { + val = tokens[i + 1]; + break; + } + } + + // 如果未找到与clause对应的键,则返回NULL + if (val == NULL) { + return NULL; + } + + // 检查值是否合法 + if (!IsLegalIntervalStr(val, numeric_only)) { + ereport(ERROR, + (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Invalid value string for clause \'%s\'", clause), + errcause("N/A"), + erraction("Please modify the calendaring string."))); + } + + return val; +} +/* + * @brief get_calendar_freqency + * Get frequency_clause value. + * frequency_clause = "FREQ" "=" ( predefined_frequency | user_defined_frequency ) + * predefined_frequency = "YEARLY" | "MONTHLY" | "WEEKLY" | "DAILY" | "HOURLY" | "MINUTELY" | "SECONDLY" + * user_defined_frequency = named_schedule + * @param calendar + * @param tokens + * @return true clause exists + * @return false clause absent + */ + //功能:根据给定的字符串数组 tokens 中的键值对信息,解析出日历的频率并将其存储在 calendar 结构体中 +static bool get_calendar_freqency(Calendar calendar, char **tokens) +{ + char *val = get_calendar_clause_val(tokens, "freq", false); + if (val == NULL) { + return false; + } + + if (pg_strcasecmp(val, "yearly") == 0) { + calendar->frequency = YEARLY; + } else if (pg_strcasecmp(val, "monthly") == 0) { + calendar->frequency = MONTHLY; + } else if (pg_strcasecmp(val, "weekly") == 0) { + calendar->frequency = WEEKLY; + } else if (pg_strcasecmp(val, "daily") == 0) { + calendar->frequency = DAILY; + } else if (pg_strcasecmp(val, "hourly") == 0) { + calendar->frequency = HOURLY; + } else if (pg_strcasecmp(val, "minutely") == 0) { + calendar->frequency = MINUTELY; + } else if (pg_strcasecmp(val, "secondly") == 0) { + calendar->frequency = SECONDLY; + } else { + pfree_ext(tokens); + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Invalid frequency value \'%s\'.", val), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } + return true; +} + +/* + * @brief get_calendar_n_interval + * Get interval_clause. + * interval_clause = "INTERVAL" "=" intervalnum + * intervalnum = 1 through 99 + * @param calendar + * @param tokens + */ + /*该函数用于解析日历的间隔值。首先将 calendar->interval 设置为默认值 1,然后调用 get_calendar_clause_val 函数获取键为 "interval" 的值,并将其转换为整数值。 + 如果获取到的值不在范围 [1, 99] 内,则释放 tokens 并抛出错误。最后将解析得到的间隔值赋值给 calendar->interval 字段。 + */ + static void get_calendar_n_interval(Calendar calendar, char **tokens) +{ + calendar->interval = 1; /* 我们总是将 interval 设置为 1 */ + char *val = get_calendar_clause_val(tokens, "interval", true); + if (val == NULL) { + return; + } + + int num = atoi(val); + if (num < 1 || num > MAX_CALENDAR_INTERVAL_NUM) { + pfree_ext(tokens); + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Interval \'%d\' not in range [1, 99].", num), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } + calendar->interval = num; +} + +/*该函数用于解析日历的月份规则。首先通过调用 get_calendar_clause_val 函数获取键为 "bymonth" 的值。 +如果获取到的值不为空指针,则应用该规则并直接返回该值。如果未指定 bymonth 规则,则根据日历的频率进行不同的处理 +*/ +static char *get_calendar_bymonth_val(Calendar calendar, char **tokens) +{ + char *val = get_calendar_clause_val(tokens, "bymonth", false); + if (val != NULL) { + /* 如果指定了 bymonth 规则,则应用该规则 */ + return val; + } + + if (calendar->frequency > MONTHLY) { + /* 如果频率较高,将每个月都视为可能的选项 */ + for (int i = 0; i < MONTHS_PER_YEAR; i++) { + calendar->bymonth[i] = i + 1; + } + calendar->month_len = MONTHS_PER_YEAR; + calendar->date_depth *= calendar->month_len; + } else if (calendar->frequency < MONTHLY) { + /* 如果频率较低,与开始日期的值同步,稍后填充 */ + calendar->month_len = 0; + } else { + /* 频率为 MONTHLY,尝试优化 */ + int mod = (calendar->interval >= MONTHS_PER_YEAR) ? calendar->interval % MONTHS_PER_YEAR : calendar->interval; + if (mod == 0) { + mod = MONTHS_PER_YEAR; + } + /* 我们需要开始月份才能确定实际的月份列表,将其设置为负数,稍后处理 */ + calendar->month_len = (MONTHS_PER_YEAR % mod == 0) ? MONTHS_PER_YEAR / mod : MONTHS_PER_YEAR; + calendar->date_depth *= calendar->month_len; + calendar->month_len *= -1; + calendar->bymonth[0] = mod; + } + return NULL; +} + +/* + * @brief get_calendar_bymonth + * Get bymonth_clause. + * bymonth_clause = "BYMONTH" "=" monthlist + * monthlist = month ( "," month)* + * month = numeric_month | char_month + * numeric_month = 1 | 2 | 3 ... 12 + * char_month = "JAN" | "FEB" | "MAR" | "APR" | "MAY" | "JUN" | "JUL" | "AUG" | "SEP" | "OCT" | "NOV" | "DEC" + * @param calendar + * @param tokens + */ + //该函数用于解析日历中的月份规则,并根据解析结果对日历的相应字段进行赋值。 +static void get_calendar_bymonth(Calendar calendar, char **tokens) +{ + const char *month_str[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}; + char *val = get_calendar_bymonth_val(calendar, tokens); + if (val == NULL) { + return; + } + + char *context = NULL; + char *tok = strtok_s(val, ",", &context); + bool used[MONTHS_PER_YEAR] = {0}; + while (tok != NULL) { + int month = 0; + if (isalpha(tok[0])) { + /* char in */ + for (int j = 0; j < MONTHS_PER_YEAR; j++) { + if (pg_strcasecmp(val, month_str[j]) == 0) { + month = j + 1; + break; + } + } + if (month == 0) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Invalid month token \'%s\'.", val), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } + } else { + /* numeric in */ + month = atoi(tok); + if (month < 1 || month > MONTHS_PER_YEAR) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Interval \'%d\' not in range [1, 12].", month), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } + } + tok = strtok_s(NULL, ",", &context); + + if (used[month - 1]) { + continue; + } + used[month - 1] = true; + } + for (int i = 0; i < MONTHS_PER_YEAR; i++) { + if (used[i]) { + calendar->bymonth[calendar->month_len] = i + 1; + calendar->month_len++; + } + } + calendar->date_depth *= (calendar->month_len == 0) ? 1 : calendar->month_len; + calendar->byfields |= INTERVAL_BYMONTH; +} + +/* + * @brief get_calendar_byweekno + * Get byweekno_clause. + * byweekno_clause = "BYWEEKNO" "=" weeknumber_list + * weeknumber_list = weeknumber ( "," weeknumber)* + * weeknumber = [minus] weekno + * weekno = 1 through 53 + * @param calendar + * @param tokens + */ + /*该函数用于处理日历中的 "BYWEEKNO" 规则。 +首先,调用 get_calendar_clause_val 函数获取 "BYWEEKNO" 规则的值。 +如果规则的值不为空,则抛出错误,提示该规则当前不支持。 +*/ +static void get_calendar_byweekno(Calendar calendar, char **tokens) +{ + char *val = get_calendar_clause_val(tokens, "byweekno", true); + if (val != NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("BYWEEKNO clause is currently unsupported."), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } +} + +/* + * @brief get_calendar_byyearday + * Get byyearday_clause. + * byyearday_clause = "BYYEARDAY" "=" yearday_list + * yearday_list = yearday ( "," yearday)* + * yearday = [minus] yeardaynum + * yeardaynum = 1 through 366 + * @param calendar + * @param tokens + */ + /*该函数用于处理日历中的 "BYYEARDAY" 规则。 +首先,调用 get_calendar_clause_val 函数获取 "BYYEARDAY" 规则的值。 +如果规则的值不为空,则抛出错误,提示该规则当前不支持。 +*/ +static void get_calendar_byyearday(Calendar calendar, char **tokens) +{ + char *val = get_calendar_clause_val(tokens, "byyearday", true); + if (val != NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("BYYEARDAY clause is currently unsupported."), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } +} + +/* + * @brief get_calendar_bydate + * Get bydate_clause. + * bydate_clause = "BYDATE" "=" date_list + * date_list = date ( "," date)* + * date = [YYYY]MMDD [ offset | span ] + * @param calendar + * @param tokens + */ + /*该函数用于处理日历中的 "BYDATE" 规则。 +首先,调用 get_calendar_clause_val 函数获取 "BYDATE" 规则的值。 +如果规则的值不为空,则抛出错误,提示该规则当前不支持。 +*/ +static void get_calendar_bydate(Calendar calendar, char **tokens) +{ + char *val = get_calendar_clause_val(tokens, "byweekno", true); + if (val != NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("BYDATE clause is currently unsupported."), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } +} + +/* +该函数用于获取 "BYMONTHDAY" 规则的值,并根据该值进行相应的处理。 +首先,调用 get_calendar_clause_val 函数获取 "BYMONTHDAY" 规则的值。 +如果规则的值不为空,则应用 "BYMONTHDAY" 规则并返回该值。 +如果日历的频率大于等于每天或者是每周一次,那么每一天都是可能的。将每一天的值存入日历结构体中,并更新相关的长度和深度。 +如果日历的频率低于每天并且不是每周一次,那么与起始日期的值同步,并稍后填充。 +最后,返回空指针表示没有要应用的 "BYMONTHDAY" 规则。 +*/ +static char *get_calendar_bymonthday_val(Calendar calendar, char **tokens) +{ + char *val = get_calendar_clause_val(tokens, "bymonthday", true); + if (val != NULL) { + /* apply bymonthday rule if bymonthday is specified */ + return val; + } + + if (calendar->frequency >= DAILY || calendar->frequency == WEEKLY) { + /* If we have higher frquency, every day is possible */ + for (int i = 0; i < DAYS_PER_MONTH + 1; i++) { + calendar->bymonthday[i] = i + 1; + } + calendar->monthday_len = DAYS_PER_MONTH + 1; + calendar->date_depth *= calendar->monthday_len; + } else { + /* If we have lower frquency, sync with start date value, fill it later */ + calendar->monthday_len = 0; + } + /* We cannot optimize any further since monthday/yearday are not perfectly periodic */ + return NULL; +} + + +/* + * @brief get_calendar_bymonth + * Get bymonthday_clause. + * bymonthday_clause = "BYMONTHDAY" "=" monthday_list + * monthday_list = monthday ( "," monthday)* + * monthday = [minus] monthdaynum + * monthdaynum = 1 through 31 + * @param calendar + * @param tokens + */ + /* + 该函数用于处理 "BYMONTHDAY" 规则的具体逻辑。 +首先,调用 get_calendar_bymonthday_val 函数获取 "BYMONTHDAY" 规则的值。 +如果规则的值为空,则直接返回。 +然后,使用逗号分隔得到每个月天的值,并进行相应的处理。 +对于每个月天的值,如果其超出了范围或为零,则抛出错误提示。 +如果月天的值为正数且已经使用过,则跳过。 +如果月天的值为负数且其绝对值已经使用过,则跳过。 +对于已经使用过的月天的值,将其存入日历结构体中,并更新相关的长度和深度。 +最后,设置相应的标志位表示已经应用了 "BYMONTHDAY" 规则。 +*/ +static void get_calendar_bymonthday(Calendar calendar, char **tokens) +{ + char *val = get_calendar_bymonthday_val(calendar, tokens); + if (val == NULL) { + return; + } + + char *context = NULL; + char *tok = strtok_s(val, ",", &context); + bool used_p[DAYS_PER_MONTH + 1] = {0}; + bool used_n[DAYS_PER_MONTH + 1] = {0}; + while (tok != NULL) { + int monthday = atoi(tok); + if (monthday < -(DAYS_PER_MONTH + 1) || monthday > (DAYS_PER_MONTH + 1) || monthday == 0) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Invalid monthday \'%d\'.", monthday), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } + tok = strtok_s(NULL, ",", &context); + + /* considering both positive and negative parts */ + if (monthday > 0 && used_p[monthday - 1]) { + continue; + } + if (monthday < 0 && used_n[(-monthday) - 1]) { + continue; + } + + if (monthday > 0) { + used_p[monthday - 1] = true; + } + if (monthday < 0) { + used_n[(-monthday) - 1] = true; + } + } + /* there is no point of making two seperate loops for all monthdays, may refactor later. */ + for (int i = 0; i <= DAYS_PER_MONTH; i++) { + if (used_p[i]) { + calendar->bymonthday[calendar->monthday_len] = i + 1; + calendar->monthday_len++; + } + } + for (int i = DAYS_PER_MONTH; i >= 0; i--) { + if (used_n[i]) { + calendar->bymonthday[calendar->monthday_len] = -(i + 1); + calendar->monthday_len++; + } + } + calendar->date_depth *= (calendar->monthday_len == 0) ? 1 : calendar->monthday_len; + calendar->byfields |= INTERVAL_BYMONTHDAY; +} + +/* + * @brief get_calendar_byday + * Get byday_clause. + * byday_clause = "BYDAY" "=" byday_list + * byday_list = byday ( "," byday)* + * byday = [weekdaynum] day + * weekdaynum = [minus] daynum + * daynum = 1 through 53 -- yearly + * daynum = 1 through 5 -- monthly + * day = "MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN" + * @param calendar + * @param tokens + *//* + * 处理 "BYDAY" 规则的逻辑 + * + * 参数: + * - calendar: 日历结构体 + * - tokens: 分词后的字符串数组 + * + * 功能: + * - 获取 "BYDAY" 规则的值,并根据该值进行相应的处理。 + * - 当规则的值不为空时,抛出错误提示,因为当前不支持 "BYDAY" 规则。 + */ +static void get_calendar_byday(Calendar calendar, char **tokens) +{ + char *val = get_calendar_clause_val(tokens, "byday", true); + if (val != NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("BYDAY clause is currently unsupported."), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } +} + +/* + * 处理 "BYHOUR" 规则的逻辑 + * + * 参数: + * - calendar: 日历结构体 + * - tokens: 分词后的字符串数组 + * + * 返回值: + * - NULL 表示没有要应用的 "BYHOUR" 规则,或已经应用过规则并且处理完毕。 + * - 非空指针表示需要应用 "BYHOUR" 规则,并返回规则的值。 + * + * 功能: + * - 获取 "BYHOUR" 规则的值,并根据该值进行相应的处理。 + * - 当规则的值不为空时,应用 "BYHOUR" 规则,并返回该值。 + * - 如果日历的频率大于每小时,那么每一小时都是可能的。将每一小时的值存入日历结构体中,并更新相关的长度和深度。 + * - 如果日历的频率低于每小时,并且不是每几小时一次,那么与起始日期的值同步,并稍后填充。 + */ +static char *get_calendar_byhour_val(Calendar calendar, char **tokens) +{ + char *val = get_calendar_clause_val(tokens, "byhour", true); + if (val != NULL) { + /* 当 byhour 规则被指定时应用规则 */ + return val; + } + + if (calendar->frequency > HOURLY) { + /* 如果频率大于每小时,那么每一小时都是可能的 */ + for (int i = 0; i < HOURS_PER_DAY; i++) { + calendar->byhour[i] = i; + } + calendar->hour_len = HOURS_PER_DAY; + calendar->time_depth *= calendar->hour_len; + } else if (calendar->frequency < HOURLY) { + /* 如果频率低于每小时,并且不是每几小时一次,与起始日期的值同步,并稍后填充 */ + calendar->hour_len = 0; + } else { + /* 频率匹配,尝试优化处理 */ + int mod = (calendar->interval >= HOURS_PER_DAY) ? calendar->interval % HOURS_PER_DAY : calendar->interval; + if (mod == 0) { + mod = HOURS_PER_DAY; + } + /* 我们需要起始小时来确定实际的小时列表,将其设置为负数,并稍后处理 */ + calendar->hour_len = (HOURS_PER_DAY % mod == 0) ? HOURS_PER_DAY / mod : HOURS_PER_DAY; + calendar->time_depth *= calendar->hour_len; + calendar->hour_len *= -1; + calendar->byhour[0] = mod; + } + return NULL; +} + + +/* + * @brief get_calendar_byhour + * Get byhour_clause. + * byhour_clause = "BYHOUR" "=" hour_list + * hour_list = hour ( "," hour)* + * hour = 0 through 23 + * @param calendar + * @param tokens + */ + /* + 该函数的功能是处理日历的 "BYHOUR" 规则。 + 根据规则的值进行相应的处理,当规则的值不为空时,应用规则并返回该值; + 当规则的值为空时,根据日历的频率、起始日期和间隔等信息确定实际的小时列表,并存储在日历结构体中。 + */ +static void get_calendar_byhour(Calendar calendar, char **tokens) +{ + char *val = get_calendar_byhour_val(calendar, tokens); + if (val == NULL) { + return; + } + + char *context = NULL; + char *tok = strtok_s(val, ",", &context); + bool used[HOURS_PER_DAY] = {0}; + while (tok != NULL) { + int hour = atoi(tok); + if (hour < 0 || hour >= HOURS_PER_DAY) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Invalid time \'%d\' o\' clock.", hour), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } + tok = strtok_s(NULL, ",", &context); + + if (used[hour]) { + continue; + } + used[hour] = true; + } + for (int i = 0; i < HOURS_PER_DAY; i++) { + if (used[i]) { + calendar->byhour[calendar->hour_len] = i; + calendar->hour_len++; + } + } + calendar->time_depth *= (calendar->hour_len == 0) ? 1 : calendar->hour_len; + calendar->byfields |= INTERVAL_BYHOUR; +} + +/*该函数的功能是处理日历的 "BYMINUTE" 规则。 +根据规则的值进行相应的处理,当规则的值不为空时,应用规则并返回该值; +当规则的值为空时,根据日历的频率、起始日期和间隔等信息确定实际的分钟列表,并存储在日历结构体中 +*/ +static char *get_calendar_byminute_val(Calendar calendar, char **tokens) +{ + char *val = get_calendar_clause_val(tokens, "byminute", true); + if (val != NULL) { + /* apply byminute rule if byminute is specified */ + return val; + } + + if (calendar->frequency > MINUTELY) { + /* If we have higher frequency, every minute is possible */ + for (int i = 0; i < MINS_PER_HOUR; i++) { + calendar->byminute[i] = i; + } + calendar->minute_len = MINS_PER_HOUR; + calendar->time_depth *= calendar->minute_len; + } else if (calendar->frequency < MINUTELY) { + /* If we have lower frequency, sync with start date value, fill it later */ + calendar->minute_len = 0; + } else { + /* frequency matched, try optimize */ + int mod = (calendar->interval >= MINS_PER_HOUR) ? calendar->interval % MINS_PER_HOUR : calendar->interval; + if (mod == 0) { + mod = MINS_PER_HOUR; + } + /* We need the start minute to figure out the ACTUAL minute list, set it to negative and deal with it later */ + calendar->minute_len = (MINS_PER_HOUR % mod == 0) ? MINS_PER_HOUR / mod : MINS_PER_HOUR; + calendar->time_depth *= calendar->minute_len; + calendar->minute_len *= -1; + calendar->byminute[0] = mod; + } + return NULL; +} + +/* + * @brief get_calendar_byminute + * Get byminute_clause. + * byminute_clause = "BYMINUTE" "=" minute_list + * minute_list = minute ( "," minute)* + * minute = 0 through 59 + * @param calendar + * @param tokens + */ + /* + get_calendar_byminute函数的功能是处理日历的 "BYMINUTE" 规则。 + 根据规则的值进行相应的处理,当规则的值不为空时,应用规则并返回该值; + 当规则的值为空时,根据日历的频率、起始日期和间隔等信息确定实际的分钟列表,并存储在日历结构体中。 + */ +static void get_calendar_byminute(Calendar calendar, char **tokens) +{ + char *val = get_calendar_byminute_val(calendar, tokens); + if (val == NULL) { + return; + } + + char *context = NULL; + char *tok = strtok_s(val, ",", &context); + bool used[MINS_PER_HOUR] = {0}; + while (tok != NULL) { + int minute = atoi(tok); + if (minute < 0 || minute >= MINS_PER_HOUR) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Invalid time \'%d\' minute [0, 59].", minute), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } + tok = strtok_s(NULL, ",", &context); + + if (used[minute]) { + continue; + } + used[minute] = true; + } + for (int i = 0; i < MINS_PER_HOUR; i++) { + if (used[i]) { + calendar->byminute[calendar->minute_len] = i; + calendar->minute_len++; + } + } + calendar->time_depth *= (calendar->minute_len == 0) ? 1 : calendar->minute_len; + calendar->byfields |= INTERVAL_BYMINUTE; +} +/* + get_calendar_bysecond_val函数的功能是处理日历的 "BYSECOND" 规则。 + 根据规则的值进行相应的处理,当规则的值不为空时,应用规则并返回该值; + 当规则的值为空时,根据日历的频率、起始日期和间隔等信息确定实际的秒钟列表,并存储在日历结构体中。 + */ +static char *get_calendar_bysecond_val(Calendar calendar, char **tokens) +{ + char *val = get_calendar_clause_val(tokens, "bysecond", true); + if (val != NULL) { + /* apply bysecond rule if bysecond is specified */ + return val; + } + + Assert(calendar->frequency <= SECONDLY); + /* Even higher frequency is unavailable */ + if (calendar->frequency < SECONDLY) { + /* If we have lower frequency, sync with start date value, fill it later */ + calendar->second_len = 0; + } else { + /* frequency matched, try optimize */ + int mod = (calendar->interval >= SECS_PER_MINUTE) ? calendar->interval % SECS_PER_MINUTE : calendar->interval; + if (mod == 0) { + mod = SECS_PER_MINUTE; + } + /* We need the start second to figure out the ACTUAL second list, set it to negative and deal with it later */ + calendar->second_len = (SECS_PER_MINUTE % mod == 0) ? SECS_PER_MINUTE / mod : SECS_PER_MINUTE; + calendar->time_depth *= calendar->second_len; + calendar->second_len *= -1; + calendar->bysecond[0] = mod; + } + return NULL; +} + +/* + * @brief get_calendar_bysecond + * Get bysecond_clause. + * bysecond_clause = "BYSECOND" "=" second_list + * second_list = second ( "," second)* + * second = 0 through 59 + * @param interval + * @param tokens + */ + /* + get_calendar_bysecond_val函数的功能是处理日历的 "BYSECOND" 规则。 + 根据规则的值进行相应的处理,当规则的值不为空时,应用规则并返回该值; + 当规则的值为空时,根据日历的频率、起始日期和间隔等信息确定实际的秒钟列表,并存储在日历结构体中。 + */ +static void get_calendar_bysecond(Calendar calendar, char **tokens) +{ + char *val = get_calendar_bysecond_val(calendar, tokens); + if (val == NULL) { + return; + } + + char *context = NULL; + char *tok = strtok_s(val, ",", &context); + bool used[SECS_PER_MINUTE] = {0}; + while (tok != NULL) { + int second = atoi(tok); + if (second < 0 || second >= SECS_PER_MINUTE) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Invalid time \'%d\' seconds [0, 59].", second), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } + tok = strtok_s(NULL, ",", &context); + + if (used[second]) { + continue; + } + used[second] = true; + } + for (int i = 0; i < SECS_PER_MINUTE; i++) { + if (used[i]) { + calendar->bysecond[calendar->second_len] = i; + calendar->second_len++; + } + } + calendar->time_depth *= (calendar->second_len == 0) ? 1 : calendar->second_len; + calendar->byfields |= INTERVAL_BYSECOND; +} + + +/* + * @brief tokenize_str + * Tokenize string with given delimiters. + * @param src source string + * @param delims delimiters + * @param fields number of tokens needed + * @return char** an array of tokens generated + */ + /* + tokenize_str函数的功能是将字符串分割为多个子字符串,并存储在字符指针数组中。 + 根据给定的分隔符和字段数,使用strtok_s函数逐个分割源字符串,将分割得到的子字符串存储在tokens数组中,并返回tokens数组。 +*/ +static char **tokenize_str(char *src, const char *delims, int fields) +{ + char **tokens = (char **)palloc0(sizeof(char *) * fields); + int count = 0; + char *context = NULL; + char *tok = strtok_s(src, delims, &context); + while (tok != NULL) { + tokens[count] = tok; + tok = strtok_s(NULL, delims, &context); + count++; + if (count >= fields) { + pfree_ext(tokens); + return NULL; + } + } + return tokens; +} + +/* +validate_field_names函数的功能是验证字段名称是否合法。 +通过比较每个字段名称与支持的字段名称列表,检查字段名称是否重复或不符合支持的字段列表。 +如果发现字段名称无效,则返回字段位置索引;如果所有字段名称都有效,则返回-1表示验证通过。 +*/ +static int validate_field_names(char **toks) +{ + bool valid = false; + const int name_pos_step = 2; + const char *supported_fields[SUPPORTED_FIELDS] = {"freq", "interval", "bymonth", "bymonthday", "byhour", + "byminute", "bysecond"}; + bool fields_used[SUPPORTED_FIELDS] = {0}; + for (int i = 0; i < MAX_CALENDAR_FIELDS; i += name_pos_step) { + for (int j = 0; j < SUPPORTED_FIELDS; j++) { + if (toks[i] == NULL) { + /* This is rare, usually token is not empty in the middle */ + valid = true; + break; + } + if (pg_strcasecmp(toks[i], supported_fields[j]) != 0) { + continue; + } + if (fields_used[j]) { + /* duplicate field name */ + valid = false; + } else { + fields_used[j] = true; + valid = true; + } + break; /* here is way pass guarding condition, break it */ + } + if (!valid) { + return i; + } + valid = false; + } + return -1; +} + +/* + * @brief interpret_calendar_interval + * The main interpreter of calendar interval. + * @param interval_str + * @return Calendar + *//* + * 解释日历字符串,生成对应的日历对象 + * 参数: + * calendar_str: 待解释的日历字符串 + * 返回值: + * 解释得到的日历对象,如果解释失败则返回NULL + */ +Calendar interpret_calendar_interval(char *calendar_str) +{ + Assert(calendar_str != NULL); + bool field = false; + + /* Make token lists */ + char **str_toks = tokenize_str(calendar_str, " =;", MAX_CALENDAR_FIELDS); + if (str_toks == NULL) { + /* 解释失败,抛出错误 */ + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Unable to parse calendaring string."), + errcause("Calendaring string is too long/invalid"), + erraction("Please modify the calendaring string."))); + } + + int pos = validate_field_names(str_toks); + if (pos >= 0) { + /* 解释失败,抛出错误 */ + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("Fail to evaluate calendaring string."), + errdetail("Incorrect/duplicate clause name '%s'.", str_toks[pos]), errcause("N/A"), + erraction("Please modify the calendaring string."))); + } + + /* 创建日历对象 */ + Calendar calendar = (Calendar)palloc0(sizeof(CalendarContext)); + + /* 主解释器 */ + field = get_calendar_freqency(calendar, str_toks); + if (!field || pg_strcasecmp(str_toks[0], "freq") != 0) { + /* + * 如果缺少频率子句(或非第一个子句),可能不是日历语法,返回NULL + */ + pfree_ext(calendar); + return NULL; + } + get_calendar_n_interval(calendar, str_toks); + + /* 解析by***子句 */ + calendar->date_depth = 1; + calendar->time_depth = 1; + get_calendar_bymonth(calendar, str_toks); + get_calendar_byweekno(calendar, str_toks); + get_calendar_byyearday(calendar, str_toks); + get_calendar_bydate(calendar, str_toks); + get_calendar_bymonthday(calendar, str_toks); + get_calendar_byday(calendar, str_toks); + get_calendar_byhour(calendar, str_toks); + get_calendar_byminute(calendar, str_toks); + get_calendar_bysecond(calendar, str_toks); + + pfree_ext(str_toks); + return calendar; +} + + +/* + * @brief get_calendar_period + * Get the calendar period in the form of Interval. + * @param calendar + * @param num_of_period + * @return Interval* + *//* + * 根据日历的频率和数量生成时间间隔 + * 参数: + * calendar: 日历对象 + * num_of_period: 期间的数量 + * 返回值: + * 生成的时间间隔对象,如果无效的频率则返回NULL + */ +static Interval *get_calendar_period(Calendar calendar, int num_of_period) +{ + /* 根据频率确定时间单位 */ + const char *freq_str = NULL; + if (calendar->frequency == YEARLY) { + freq_str = "years"; + } else if (calendar->frequency == MONTHLY) { + freq_str = "months"; + } else if (calendar->frequency == WEEKLY) { + freq_str = "weeks"; + } else if (calendar->frequency == DAILY) { + freq_str = "days"; + } else if (calendar->frequency == HOURLY) { + freq_str = "hours"; + } else if (calendar->frequency == MINUTELY) { + freq_str = "minutes"; + } else if (calendar->frequency == SECONDLY) { + freq_str = "seconds"; + } else { + /* 依赖于上层的检查,当达到最大日历深度时会停止 */ + return NULL; + } + + /* 构造时间间隔对象并返回 */ + return calendar_construct_interval(freq_str, calendar->interval * num_of_period); +} + +/* + * 复制日期数组的批次数据 + * 参数: + * dates: 目标时间戳数组 + * nvals: 批次数量 + * cnt: 存在的值的个数 + * date_in: 可选,仅在dates为空时有效 + */ +static void copy_calendar_dates(TimestampTz *timeline, int nvals, int cnt) +{ + /* 复制现有时间戳的nvals个拷贝 */ + if (nvals <= 1) { + return; + } + for (int i = 0; i < cnt; i++) { + for (int j = 1; j < nvals; j++) { + timeline[i + (j * cnt)] = timeline[i]; + } + } +} + +/* + * 验证给定的月份日期是否有效 + * 参数: + * year: 年份 + * month: 月份 + * mday: 日期 + * 返回值: + * 如果是有效的月份日期返回true,否则返回false + */ +static bool validate_calendar_monthday(int year, int month, int mday) +{ + /* 每个月的天数 */ + bool month_31[MONTHS_PER_YEAR] = {1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1}; + + /* 检查日期是否超出范围 */ + if ((!month_31[month - 1] && mday > 30) || (month_31[month - 1] && mday > 31)) { + return false; + } + + /* 闰年的特殊处理 */ + if ((!isleap(year) && month == 2 && mday > 29) || (isleap(year) && month == 2 && mday > 28)) { + return false; + } + + /* 月份日期可以为负数 */ + if (!isleap(year) && month == 2 && mday < -28) { + return false; + } + if (isleap(year) && month == 2 && mday < -29) { + return false; + } + if (!month_31[month - 1] && mday < -30) { + return false; + } + + return true; +} + + +/* + * @brief evaluate_calendar_bymonth + * Evaluate bymonth field. + * @param calendar + * @param dates + * @param cnt + */ +static void evaluate_calendar_bymonth(Calendar calendar, TimestampTz *timeline, int *cnt) +{ + Assert(*cnt <= calendar->date_depth); + if (*cnt == 0) { + return; + } + + Interval *interval = NULL; + int chunk = *cnt; + TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("month"), calendar->ref_date); + + // 检查是否存在bymonthday,如果不存在需要根据当前时间的月份构造bymonthday,保证至少有一个月份符合条件。 + if (calendar->month_len == 0) { + calendar->bymonth[0] = calendar->tm.tm_mon; + calendar->month_len = 1; + } else if (calendar->month_len < 0) { + // 如果bymonth为负数,则每隔abs(bymonth)个月执行一次。 + int mod = calendar->bymonth[0]; + int start = (calendar->tm.tm_mon - 1) - (((calendar->tm.tm_mon - 1) / mod) * mod) + 1; + calendar->month_len = 0; + while (start < MONTHS_PER_YEAR) { + calendar->bymonth[calendar->month_len] = start; + start += mod; + calendar->month_len++; + } + } + + *cnt = 0; + copy_calendar_dates(timeline, calendar->month_len, chunk); + for (int i = 0; i < calendar->month_len; i++) { + interval = calendar_construct_interval("months", calendar->bymonth[i] - 1); + for (int j = i * chunk; j < (i + 1) * chunk; j++) { + timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); + if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { + (*cnt)++; + } + } + pfree_ext(interval); + } +} + +/** + * @brief evaluate_calendar_byweekno + * Evaluate byweekno field. + * @param calendar + * @param timeline + * @param cnt + */ +static void evaluate_calendar_byweekno(Calendar calendar, TimestampTz *timeline, int *cnt) +{ + // 目前未实现byweekno计算,直接返回。 + return; +} + +/** + * @brief evaluate_calendar_byyearday + * Evaluate byyearday field. + * @param calendar + * @param timeline + * @param cnt + */ +static void evaluate_calendar_byyearday(Calendar calendar, TimestampTz *timeline, int *cnt) +{ + // 目前未实现byyearday计算,直接返回。 + return; +} + +/** + * @brief evaluate_calendar_bymonthday + * Evaluate bymonthday field. + * @param calendar + * @param timeline + * @param cnt + */ +static void evaluate_calendar_bymonthday(Calendar calendar, TimestampTz *timeline, int *cnt) +{ + Assert(*cnt <= calendar->date_depth); + if (*cnt == 0) { + return; + } + + int chunk = *cnt; + Interval *interval = NULL; + TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("day"), calendar->ref_date); + + // 检查是否存在bymonthday,如果不存在需要根据当前时间的月份构造bymonthday,保证至少有一个月份符合条件。 + if (calendar->monthday_len == 0) { + calendar->bymonthday[0] = calendar->tm.tm_mday; + calendar->monthday_len = 1; + } + + *cnt = 0; + int tz = 0; + fsec_t fsec; + struct pg_tm tt, *tm = &tt; /* POSIX time struct, see NOTE above */ + copy_calendar_dates(timeline, calendar->monthday_len, chunk); + for (int i = 0; i < calendar->monthday_len; i++) { + if (calendar->bymonthday[i] < 0) { + interval = calendar_construct_interval("months", 1); + } else { + interval = calendar_construct_interval("days", calendar->bymonthday[i] - 1); + } + for (int j = i * chunk; j < (i + 1) * chunk; j++) { + if (timestamp2tm(timeline[j], &tz, tm, &fsec, NULL, NULL) != 0) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("Cannot evaluate calendar clause."), errdetail("Invalid timeline."), + errcause("N/A"), erraction("Please modify the calendaring string."))); + } + if (!validate_calendar_monthday(tm->tm_year, tm->tm_mon, calendar->bymonthday[i])) { + // 无效的月天数,直接跳过。 + continue; + } + timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); + if (calendar->bymonthday[i] <= 0) { + // 如果月天数为负数,则表示倒数第n天。 + pfree_ext(interval); + interval = calendar_construct_interval("days", -(calendar->bymonthday[i])); + timeline[*cnt] = DatumGetTimestampTz(timestamp_mi_interval(timeline[j], interval)); + } + if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { + (*cnt)++; + } + } + pfree_ext(interval); + } +} +/* + * @brief evaluate_calendar_byhour + * 评估按小时的字段。 + * @param calendar 日历参数 + * @param timeline 时间线 + * @param cnt 计数器 + */ +static void evaluate_calendar_byhour(Calendar calendar, TimestampTz *timeline, int *cnt) +{ + Assert(*cnt <= calendar->time_depth); + if (*cnt == 0) { + return; + } + + Interval *interval = NULL; + int chunk = *cnt; + TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("hour"), calendar->ref_date); + + if (calendar->hour_len == 0) { + calendar->byhour[0] = calendar->tm.tm_hour; + calendar->hour_len = 1; + } else if (calendar->hour_len < 0) { + int mod = calendar->byhour[0]; + int start = calendar->tm.tm_hour - ((calendar->tm.tm_hour / mod) * mod); + calendar->hour_len = 0; + while (start < HOURS_PER_DAY) { + calendar->byhour[calendar->hour_len] = start; + start += mod; + calendar->hour_len++; + } + } + + *cnt = 0; + copy_calendar_dates(timeline, calendar->hour_len, chunk); + for (int i = 0; i < calendar->hour_len; i++) { + interval = calendar_construct_interval("hours", calendar->byhour[i]); + for (int j = i * chunk; j < (i + 1) * chunk; j++) { + timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); + if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { + (*cnt)++; + } + } + pfree_ext(interval); + } +} + +/* + * @brief evaluate_calendar_byminute + * 评估按分钟的字段。 + * @param calendar 日历参数 + * @param timeline 时间线 + * @param cnt 计数器 + */ +static void evaluate_calendar_byminute(Calendar calendar, TimestampTz *timeline, int *cnt) +{ + Assert(*cnt <= calendar->time_depth); + if (*cnt == 0) { + return; + } + + Interval *interval = NULL; + int chunk = *cnt; + TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("minute"), calendar->ref_date); + + if (calendar->minute_len == 0) { + calendar->byminute[0] = calendar->tm.tm_min; + calendar->minute_len = 1; + } else if (calendar->minute_len < 0) { + int mod = calendar->byminute[0]; + int start = calendar->tm.tm_min - ((calendar->tm.tm_min / mod) * mod); + calendar->minute_len = 0; + while (start < MINS_PER_HOUR) { + calendar->byminute[calendar->minute_len] = start; + start += mod; + calendar->minute_len++; + } + } + + *cnt = 0; + copy_calendar_dates(timeline, calendar->minute_len, chunk); + for (int i = 0; i < calendar->minute_len; i++) { + interval = calendar_construct_interval("minutes", calendar->byminute[i]); + for (int j = i * chunk; j < (i + 1) * chunk; j++) { + timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); + if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { + (*cnt)++; + } + } + pfree_ext(interval); + } +} +/* + * @brief evaluate_calendar_bysecond + * 评估bysecond字段。 + * @param calendar 日历对象 + * @param timeline 时间线数组 + * @param cnt 计数器 + */ +static void evaluate_calendar_bysecond(Calendar calendar, TimestampTz *timeline, int *cnt) +{ + Assert(*cnt <= calendar->time_depth); + if (*cnt == 0) { + return; + } + + Interval *interval = NULL; + int chunk = *cnt; + TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("second"), calendar->ref_date); + + if (calendar->second_len == 0) { + calendar->bysecond[0] = calendar->tm.tm_sec; + calendar->second_len = 1; + } else if (calendar->second_len < 0) { + int mod = calendar->bysecond[0]; + int start = calendar->tm.tm_sec - ((calendar->tm.tm_sec / mod) * mod); + calendar->second_len = 0; + while (start < SECS_PER_MINUTE) { + calendar->bysecond[calendar->second_len] = start; + start += mod; + calendar->second_len++; + } + } + + *cnt = 0; + copy_calendar_dates(timeline, calendar->second_len, chunk); + for (int i = 0; i < calendar->second_len; i++) { + interval = calendar_construct_interval("seconds", calendar->bysecond[i]); + for (int j = i * chunk; j < (i + 1) * chunk; j++) { + timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); + if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { + (*cnt)++; + } + } + pfree_ext(interval); + } +} + +/* + * @brief fastforward_calendar_period + * 快速前进到日期后的前一天,以节省计算资源。 + * @param calendar 日历对象 + * @param start_date 起始日期 + * @param date_after 之后的日期 + */ +static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_date, TimestampTz date_after) +{ + Interval *interval = calendar_construct_interval("second", 1); + if (timestamptz_cmp_internal(*start_date, date_after) >= 0) { + /* 不需要倒回,也不相等 */ + calendar->ref_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, interval)); + pfree_ext(interval); + return; + } + + TimestampTz new_start_date; + Interval *period = get_calendar_period(calendar); + if (period == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("无法评估日历子句。"), errdetail("错误的间隔子句。"), + errcause("N/A"), erraction("请修改日历字符串。"))); + } + Datum pace_datum = DirectFunctionCall2(interval_part, CStringGetTextDatum("epoch"), + PointerGetDatum(period)); + int pace = (int)DatumGetFloat8(pace_datum); + pfree_ext(period); + + long elapsed_sec = 0; + int elapsed_usec = 0; + TimestampDifference(*start_date, date_after, &elapsed_sec, &elapsed_usec); + + /* 快速前进的秒数 = floor(elapsed / pace) * pace */ + int num_of_periods = (elapsed_sec / pace); + if (num_of_periods >= 1) { + Interval *ff_span = get_calendar_period(calendar, num_of_periods); + if (ff_span == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("无法评估日历子句。"), errdetail("错误的间隔子句。"), + errcause("N/A"), erraction("请修改日历字符串。"))); + } + new_start_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, ff_span)); + pfree_ext(ff_span); + *start_date = new_start_date; + } + calendar->ref_date = DatumGetTimestampTz(timestamp_pl_interval(date_after, interval)); + pfree_ext(interval); +}/* + * @brief evaluate_calendar_bysecond + * 对bysecond字段进行评估,生成时间线数组。 + * @param calendar 日历对象 + * @param timeline 时间线数组 + * @param cnt 计数器 + */ +static void evaluate_calendar_bysecond(Calendar calendar, TimestampTz *timeline, int *cnt) +{ + Assert(*cnt <= calendar->time_depth); + if (*cnt == 0) { + return; + } + + Interval *interval = NULL; + int chunk = *cnt; + TimestampTz ref = truncate_calendar_date(CStringGetTextDatum("second"), calendar->ref_date); + + // 如果bysecond长度为0,则默认使用当前秒作为bysecond值(长度为1) + if (calendar->second_len == 0) { + calendar->bysecond[0] = calendar->tm.tm_sec; + calendar->second_len = 1; + } + // 如果bysecond长度为负数,则以模值作为起点,生成连续的秒数 + else if (calendar->second_len < 0) { + int mod = calendar->bysecond[0]; + int start = calendar->tm.tm_sec - ((calendar->tm.tm_sec / mod) * mod); + calendar->second_len = 0; + while (start < SECS_PER_MINUTE) { + calendar->bysecond[calendar->second_len] = start; + start += mod; + calendar->second_len++; + } + } + + *cnt = 0; + copy_calendar_dates(timeline, calendar->second_len, chunk); + for (int i = 0; i < calendar->second_len; i++) { + interval = calendar_construct_interval("seconds", calendar->bysecond[i]); + for (int j = i * chunk; j < (i + 1) * chunk; j++) { + timeline[*cnt] = DatumGetTimestampTz(timestamp_pl_interval(timeline[j], interval)); + if (timestamp_cmp_internal(timeline[*cnt], ref) >= 0) { + (*cnt)++; + } + } + pfree_ext(interval); + } +} + +/* + * @brief fastforward_calendar_period + * 快速前进到日期后的前一天,以节省计算资源。 + * @param calendar 日历对象 + * @param start_date 起始日期 + * @param date_after 之后的日期 + */ +static void fastforward_calendar_period(Calendar calendar, TimestampTz *start_date, TimestampTz date_after) +{ + Interval *interval = calendar_construct_interval("second", 1); + if (timestamptz_cmp_internal(*start_date, date_after) >= 0) { + /* 不需要倒回,也不相等 */ + calendar->ref_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, interval)); + pfree_ext(interval); + return; + } + + TimestampTz new_start_date; + Interval *period = get_calendar_period(calendar); + if (period == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("无法评估日历子句。"), errdetail("错误的间隔子句。"), + errcause("N/A"), erraction("请修改日历字符串。"))); + } + Datum pace_datum = DirectFunctionCall2(interval_part, CStringGetTextDatum("epoch"), + PointerGetDatum(period)); + int pace = (int)DatumGetFloat8(pace_datum); + pfree_ext(period); + + long elapsed_sec = 0; + int elapsed_usec = 0; + TimestampDifference(*start_date, date_after, &elapsed_sec, &elapsed_usec); + + /* 快速前进的秒数 = floor(elapsed / pace) * pace */ + int num_of_periods = (elapsed_sec / pace); + if (num_of_periods >= 1) { + Interval *ff_span = get_calendar_period(calendar, num_of_periods); + if (ff_span == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("无法评估日历子句。"), errdetail("错误的间隔子句。"), + errcause("N/A"), erraction("请修改日历字符串。"))); + } + new_start_date = DatumGetTimestampTz(timestamp_pl_interval(*start_date, ff_span)); + pfree_ext(ff_span); + *start_date = new_start_date; + } + calendar->ref_date = DatumGetTimestampTz(timestamp_pl_interval(date_after, interval)); + pfree_ext(interval); +} + +/* + * @brief recheck_calendar_period + * 通常需要对给定的时间戳进行二次检查,以确定是否与原始频率相符。 + * @param calendar 日历对象 + * @param start_date 起始日期 + * @param next_date 下一个日期 + * @return true 相符 + * @return false 不相符 + */ +static bool recheck_calendar_period(Calendar calendar, TimestampTz start_date, TimestampTz next_date) +{ + const char *field_str = NULL; + int multiplier = 1; + if (calendar->frequency == YEARLY) { + field_str = "year"; + } else if (calendar->frequency == MONTHLY) { + field_str = "month"; + } else if (calendar->frequency == WEEKLY) { + field_str = "day"; + multiplier = DAYS_PER_WEEK; + } else if (calendar->frequency == DAILY) { + field_str = "day"; + } else if (calendar->frequency == HOURLY) { + field_str = "hour"; + } else if (calendar->frequency == MINUTELY) { + field_str = "minute"; + } else if (calendar->frequency == SECONDLY) { + field_str = "second"; + } else { + /* rely on upper level checks, which will eventually reach the MAX_CALENDAR_DEPTH and stop */ + return false; + } + + start_date = truncate_calendar_date(CStringGetTextDatum(field_str), start_date); + next_date = truncate_calendar_date(CStringGetTextDatum(field_str), next_date); + + int64 elapsed = timestamp_diff_internal(cstring_to_text(field_str), start_date, next_date, true); + if (elapsed % (calendar->interval * multiplier) == 0) { + return true; + } + return false; +} + +/* + * @brief timestamp_cmp_func + * 比较函数,用于qsort排序 + * @param dt1 时间戳1 + * @param dt2 时间戳2 + * @return int 比较结果 + */ +static int timestamp_cmp_func(const void *dt1, const void *dt2) +{ + TimestampTz *t1 = (TimestampTz *)dt1; + TimestampTz *t2 = (TimestampTz *)dt2; + + return timestamp_cmp_internal(*t1, *t2); +} + +/* + * @brief find_nearest_calendar_time + * 从所有有效的时间戳中找到最近的时间戳。 + * @param calendar 日历对象 + * @param timeline 时间戳列表 + * @param start 起始时间 + * @param cnt 有效的最终时间戳数量 + * @param nearest 最近的时间戳 + * @return true 找到 + * @return false 未找到 + */ +static bool find_nearest_calendar_time(Calendar calendar, TimestampTz *timeline, TimestampTz start, int cnt, + TimestampTz *nearest) +{ + /* 对时间戳进行排序 */ + qsort(timeline, cnt, sizeof(TimestampTz), timestamp_cmp_func); + + for (int i = 0; i < cnt; i++) { + /* 必须大于ref_date,并且与原始频率相符合 */ + if (timestamp_cmp_internal(timeline[i], calendar->ref_date) >= 0 && + recheck_calendar_period(calendar, start, timeline[i])) { + *nearest = timeline[i]; + return true; + } + } + return false; +} +/* + * @brief evaluate_calendar_period + * 评估日历的周期部分,生成时间线数组。 + * @param calendar 日历对象 + * @param timeline 时间线数组 + * @param sub_timeline 子时间线数组 + * @param start_date 起始日期 + * @param next_date 下一个日期 + * @return true 成功 + * @return false 失败 + */ +static bool evaluate_calendar_period(Calendar calendar, TimestampTz *timeline, TimestampTz *sub_timeline, + TimestampTz start_date, TimestampTz *next_date) +{ + int date_cnt = 1; + evaluate_calendar_bymonth(calendar, timeline, &date_cnt); + evaluate_calendar_byweekno(calendar, timeline, &date_cnt); + evaluate_calendar_byyearday(calendar, timeline, &date_cnt); + evaluate_calendar_bymonthday(calendar, timeline, &date_cnt); + + /* 对时间线进行排序,以便在找到晚于当前时间的时间戳后立即中止 */ + qsort(timeline, date_cnt, sizeof(TimestampTz), timestamp_cmp_func); + + /* + * 使用已排序的时间线(日期部分)为每一天生成匹配的时间戳。 + */ + int time_cnt; + for (int i = 0; i < date_cnt; i++) { + time_cnt = 1; + sub_timeline[0] = timeline[i]; /* 起始日期 */ + evaluate_calendar_byhour(calendar, sub_timeline, &time_cnt); + evaluate_calendar_byminute(calendar, sub_timeline, &time_cnt); + evaluate_calendar_bysecond(calendar, sub_timeline, &time_cnt); + if (find_nearest_calendar_time(calendar, sub_timeline, start_date, time_cnt, next_date)) { + return true; + } + } + return false; +} + +/* + * @brief prepare_calendar_period + * 准备日历的周期部分。 + * @param calendar 日历对象 + * @param base_date 基准日期 + * @param timeline 时间线数组 + */ +static void prepare_calendar_period(Calendar calendar, TimestampTz base_date, TimestampTz *timeline) +{ + Assert(calendar != NULL); + Assert(calendar->frequency <= MAX_FREQ); + Assert(calendar->frequency >= YEARLY); + Assert(calendar->date_depth > 0); + Assert(calendar->time_depth > 0); + timeline[0] = truncate_calendar_date(CStringGetTextDatum("year"), TimestampTzGetDatum(base_date)); + + if (calendar->date_depth > MAX_CALENDAR_DATE_DEPTH || calendar->time_depth > SECS_PER_DAY) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("无法评估日历子句。"), + errdetail("调度程序尝试在可预见的未来找到有效日期的次数用尽。"), + errcause("N/A"), erraction("请修改日历字符串。"))); + } + + fsec_t fsec; + struct pg_tm tt, *tm = &tt; /* POSIX时间结构,参见上面的NOTE */ + int tz; + if (timestamp2tm(base_date, &tz, tm, &fsec, NULL, NULL) != 0) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("无法评估日历子句。"), errdetail("无法截断起始日期。"), + errcause("N/A"), erraction("请修改日历字符串。"))); + } + calendar->tm = tt; + calendar->fsec = fsec; + + if (timestamp_cmp_internal(timeline[0], calendar->ref_date) > 0) { + calendar->ref_date = timeline[0]; + } +}/* + * @brief get_next_calendar_period + * 获取下一个日历周期。通常是一年,但如果间隔很大,可以是多年。 + * @param calendar 日历对象 + * @param base_date 基准日期 + * @return TimestampTz 下一个日历周期的起始日期 + */ +static TimestampTz get_next_calendar_period(Calendar calendar, TimestampTz base_date) +{ + int span_multiplier = 1; + if (calendar->frequency == MONTHLY && calendar->interval > MONTHS_PER_YEAR) { + span_multiplier = calendar->interval / MONTHS_PER_YEAR; + } + Interval *span = calendar_construct_interval("years", span_multiplier); + base_date = DatumGetTimestampTz(timestamp_pl_interval(base_date, span)); + pfree_ext(span); + return base_date; +} + + +/* + * @brief evaluate_calendar_interval + * 根据起始日期计算下一个日期。 + * @param calendar 日历对象 + * @param start_date 起始日期 + * @return TimestampTz 下一个日期 + */ +static TimestampTz evaluate_calendar_interval(Calendar calendar, TimestampTz start_date) +{ + if (calendar == NULL) { + return start_date; + } + + int try_count = 0; + TimestampTz next_date = start_date; + TimestampTz base_date = start_date; /* 每个周期的基准日期,在循环中更新 */ + TimestampTz *timeline = (TimestampTz *)palloc0(calendar->date_depth * sizeof(TimestampTz)); + TimestampTz *sub_timeline = (TimestampTz *)palloc0((calendar->time_depth + 1) * sizeof(TimestampTz)); + while (try_count < YEARS_PER_CENTURY) { + prepare_calendar_period(calendar, base_date, timeline); + if (evaluate_calendar_period(calendar, timeline, sub_timeline, start_date, &next_date)) { + break; + } + base_date = get_next_calendar_period(calendar, base_date); + CHECK_FOR_INTERRUPTS(); + try_count++; + } + + pfree_ext(timeline); + pfree_ext(sub_timeline); + if (try_count >= YEARS_PER_CENTURY) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("无法评估日历子句。"), errdetail("日历子句太深。"), + errcause("N/A"), erraction("请修改日历字符串。"))); + } + return next_date; +}/* + * @brief evaluate_repeat_interval + * 根据起始日期和日历字符串计算下一个日期。 + * @param calendar_in 日历字符串 + * @param start_date 起始日期 + * @return Datum 下一个日期 + */ +Datum evaluate_repeat_interval(Datum calendar_in, Datum start_date, Datum date_after) +{ + char *calendar_str = text_to_cstring(DatumGetTextP(calendar_in)); + if (strlen(calendar_str) > (size_t)MAX_CALENDAR_STR_LEN) { + pfree_ext(calendar_str); + return calendar_in; + } + + TimestampTz start_date_raw = DatumGetTimestampTz(start_date); + Calendar calendar = interpret_calendar_interval(calendar_str); + + /* 移除毫秒部分 */ + start_date_raw = truncate_calendar_date(CStringGetTextDatum("second"), TimestampTzGetDatum(start_date_raw)); + + /* 如果指定了date_after,则快进到start_date之后的日期 */ + if (calendar != NULL) { + /* 移除毫秒部分 */ + date_after = truncate_calendar_date(CStringGetTextDatum("second"), TimestampTzGetDatum(date_after)); + fastforward_calendar_period(calendar, &start_date_raw, DatumGetTimestampTz(date_after)); + } + TimestampTz next_date = evaluate_calendar_interval(calendar, start_date_raw); + + pfree_ext(calendar_str); + if (calendar == NULL) { + /* 如果不是日历间隔表达式,则直接返回,不需要释放calendar */ + return calendar_in; + } else { + pfree_ext(calendar); + } + return TimestampTzGetDatum(next_date); +} + +/* + * @brief evaluate_calendar_string_internal + * eval_calendar_string的接口函数。 + * @return Datum + */ +Datum evaluate_calendar_string_internal(PG_FUNCTION_ARGS) +{ + Datum string = PG_GETARG_DATUM(0); /* 日历字符串 */ + Datum start_date = PG_GETARG_DATUM(1); /* 起始日期 */ + Datum date_after = PG_GETARG_DATUM(2); /* 返回日期之后的日期 */ + Datum new_next_date = evaluate_repeat_interval(string, start_date, date_after); + PG_RETURN_DATUM(new_next_date); +} -- 2.34.1 From 1667b7097ff34885932c5a760c8495e5e0cbba96 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:15:13 +0800 Subject: [PATCH 47/56] Delete 'src/gausskernel/process/job/gs_job_manager.cpp' --- .../process/job/gs_job_manager.cpp | 1728 ----------------- 1 file changed, 1728 deletions(-) delete mode 100644 src/gausskernel/process/job/gs_job_manager.cpp diff --git a/src/gausskernel/process/job/gs_job_manager.cpp b/src/gausskernel/process/job/gs_job_manager.cpp deleted file mode 100644 index feca70a7d..000000000 --- a/src/gausskernel/process/job/gs_job_manager.cpp +++ /dev/null @@ -1,1728 +0,0 @@ -/* - * 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. - * ------------------------------------------------------------------------- - * - * gs_job_manager.cpp - * Functions to run/stop/execute/drop dbe jobs. - * - * IDENTIFICATION - * src/gausskernel/process/job/gs_job_manager.cpp - * - * ------------------------------------------------------------------------- - */ - -#include "postgres.h" -#include "knl/knl_variable.h" -#include -#include "access/sysattr.h" -#include "access/xact.h" -#include "catalog/indexing.h" -#include "catalog/namespace.h" -#include "catalog/objectaccess.h" -#include "catalog/pg_collation.h" -#include "catalog/pg_type.h" -#include "commands/alter.h" -#include "commands/comment.h" -#include "commands/dbcommands.h" -#include "commands/extension.h" -#include "commands/schemacmds.h" -#include "executor/spi.h" -#include "funcapi.h" -#include "mb/pg_wchar.h" -#include "miscadmin.h" -#include "pgxc/pgxc.h" -#include "tcop/utility.h" -#include "utils/builtins.h" -#include "utils/dbe_scheduler.h" -#include "utils/fmgroids.h" -#include "utils/formatting.h" -#include "utils/lsyscache.h" -#include "utils/rel.h" -#include "utils/rel_gs.h" -#include "utils/snapmgr.h" -#include "access/heapam.h" -#include "access/tableam.h" -#include "catalog/pg_job.h" -#include "catalog/pg_job_proc.h" -#include "catalog/pg_authid.h" -#include "catalog/pg_database.h" -#include "catalog/gs_job_argument.h" -#include "catalog/gs_job_attribute.h" -#include "fmgr.h" -#include "utils/syscache.h" -#include "pgxc/execRemote.h" - -/* Run job methods */ -static bool run_sql_job(Datum job_name, StringInfoData *buf); -static bool run_procedure_job(Datum job_name, StringInfoData *buf); -static char *run_external_job(Datum job_name); - -/* - * @brief delete_by_syscache - * Perform a simple heap delete by searching syscache. - * @param rel Target relation - * @param object_name Delete by key - * @param cache_id Cache ID - */ -static void delete_by_syscache(Relation rel, const Datum object_name, SysCacheIdentifier cache_id) -{ - CatCList *tuples = SearchSysCacheList1(cache_id, object_name); - if (tuples == NULL) { - return; - } - for (int i = 0; i < tuples->n_members; i++) { - HeapTuple tuple = t_thrd.lsc_cxt.FetchTupleFromCatCList(tuples, i); - simple_heap_delete(rel, &tuple->t_self); - } - ReleaseSysCacheList(tuples); -} - -/* - * @brief delete_from_attribute - * Delete from gs_job_attribute. - * @param object_name - */ -void delete_from_attribute(const Datum object_name) -{ - Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); - delete_by_syscache(gs_job_attribute_rel, object_name, JOBATTRIBUTENAME); - heap_close(gs_job_attribute_rel, NoLock); -} - -/* - * @brief delete_from_argument - * Delete from gs_job_argument. - * @param job_name - */ -void delete_from_argument(const Datum object_name) -{ - Relation rel = heap_open(GsJobArgumentRelationId, RowExclusiveLock); - delete_by_syscache(rel, object_name, JOBARGUMENTNAME); - heap_close(rel, NoLock); -} - -/* - * @brief delete_from_job - * Delete from pg_job. - * @param job_name - */ -void delete_from_job(const Datum job_name) -{ - Relation rel = heap_open(PgJobRelationId, RowExclusiveLock); - HeapTuple tuple = search_from_pg_job(rel, job_name); - if (tuple != NULL) { - simple_heap_delete(rel, &tuple->t_self); - } - heap_close(rel, NoLock); -} - -/* - * @brief delete_from_job_proc - * Delete from pg_job_proc. - * @param job_name - */ -void delete_from_job_proc(const Datum job_name) -{ - Relation rel = heap_open(PgJobProcRelationId, RowExclusiveLock); - HeapTuple tuple = search_from_pg_job_proc_no_exception(rel, job_name); - if (tuple != NULL) { - simple_heap_delete(rel, &tuple->t_self); - } - heap_close(rel, NoLock); -} - -HeapTuple search_from_pg_job(Relation pg_job_rel, Datum job_name) -{ - ScanKeyInfo scan_key_info1; - scan_key_info1.attribute_value = job_name; - scan_key_info1.attribute_number = Anum_pg_job_job_name; - scan_key_info1.procedure = F_TEXTEQ; - ScanKeyInfo scan_key_info2; - scan_key_info2.attribute_value = PointerGetDatum(u_sess->proc_cxt.MyProcPort->database_name); - scan_key_info2.attribute_number = Anum_pg_job_dbname; - scan_key_info2.procedure = F_NAMEEQ; - - List *tuples = search_by_sysscan_2(pg_job_rel, &scan_key_info1, &scan_key_info2); - if (tuples == NIL) { - return NULL; - } - Assert(list_length(tuples) == 1); - if (list_length(tuples) != 1) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("find %d tuples match job_name %s in system table pg_job.", list_length(tuples), - TextDatumGetCString(job_name)), - errdetail("N/A"), errcause("job name is not exist"), erraction("Please check job_name"))); - } - HeapTuple tuple = (HeapTuple)linitial(tuples); - list_free_ext(tuples); - return tuple; -} - -/* - * @brief update_pg_job - * Update pg_job. - * @param job_name - * @param attribute_number - * @param attribute_value - */ -void update_pg_job(Datum job_name, int attribute_number, Datum attribute_value, bool isnull) -{ - Datum values[Natts_pg_job]; - bool nulls[Natts_pg_job]; - bool replaces[Natts_pg_job]; - errno_t rc = memset_s(replaces, sizeof(replaces), 0, sizeof(replaces)); - securec_check_c(rc, "\0", "\0"); - replaces[attribute_number - 1] = true; - if (!isnull) { - values[attribute_number - 1] = attribute_value; - nulls[attribute_number - 1] = false; - } else { - nulls[attribute_number - 1] = true; - } - - Relation pg_job_rel = heap_open(PgJobRelationId, RowExclusiveLock); - HeapTuple oldtuple = search_from_pg_job(pg_job_rel, job_name); - if (!HeapTupleIsValid(oldtuple)) { - heap_close(pg_job_rel, NoLock); - return; - } - - HeapTuple newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(pg_job_rel), values, nulls, replaces); - simple_heap_update(pg_job_rel, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(pg_job_rel, newtuple); - heap_close(pg_job_rel, NoLock); - heap_freetuple_ext(newtuple); - heap_freetuple_ext(oldtuple); -} - -/* - * @brief update_pg_job_multi_columns - * Update multple columns from pg_job. - * @param job_name - * @param attribute_numbers attributes needed to be updated - * @param attribute_values attribute values(corresponds to attribute numbers) - * @param n number of columns - */ -void update_pg_job_multi_columns(const Datum job_name, const int *attribute_numbers, const Datum *attribute_values, - const bool *isnull, int n) -{ - Datum values[Natts_pg_job]; - bool nulls[Natts_pg_job]; - bool replaces[Natts_pg_job]; - error_t rc = memset_s(replaces, sizeof(replaces), 0, sizeof(replaces)); - securec_check_c(rc, "\0", "\0"); - for (int i = 0; i < n; i++) { - replaces[attribute_numbers[i] - 1] = true; - if (!isnull[i]) { - values[attribute_numbers[i] - 1] = attribute_values[i]; - nulls[attribute_numbers[i] - 1] = false; - } else { - nulls[attribute_numbers[i] - 1] = true; - } - } - - - Relation pg_job_rel = heap_open(PgJobRelationId, RowExclusiveLock); - HeapTuple oldtuple = search_from_pg_job(pg_job_rel, job_name); - if (!HeapTupleIsValid(oldtuple)) { - heap_close(pg_job_rel, NoLock); - return; - } - - HeapTuple newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(pg_job_rel), values, nulls, replaces); - simple_heap_update(pg_job_rel, &newtuple->t_self, newtuple); - CatalogUpdateIndexes(pg_job_rel, newtuple); - heap_close(pg_job_rel, NoLock); - heap_freetuple_ext(newtuple); - heap_freetuple_ext(oldtuple); -} - -/* - * @brief search_related_attribute - * Search all attributes that is somewhat related to the object. - * @param gs_job_attribute_rel - * @param attribute_name - * @param attribute_value - * @return List* List of related attribute tuples. - */ -List *search_related_attribute(Relation gs_job_attribute_rel, Datum attribute_name, Datum attribute_value) -{ - /* Job itself does not need related attributes */ - if (attribute_name == (Datum)0) { - return NIL; - } - ScanKeyInfo scan_key_info1; - scan_key_info1.attribute_value = attribute_name; - scan_key_info1.attribute_number = Anum_gs_job_attribute_attribute_name; - scan_key_info1.procedure = F_TEXTEQ; - ScanKeyInfo scan_key_info2; - scan_key_info2.attribute_value = attribute_value; - scan_key_info2.attribute_number = Anum_gs_job_attribute_attribute_value; - scan_key_info2.procedure = F_TEXTEQ; - List *tuples = search_by_sysscan_2(gs_job_attribute_rel, &scan_key_info1, &scan_key_info2); - return tuples; -} - -/* - * @brief disable_related_jobs_force - */ -static void disable_related_jobs_force(Relation gs_job_attribute_rel, List *disable_job_names) -{ - ListCell *lc = NULL; - foreach (lc, disable_job_names) { - Datum job_name = PointerGetDatum(lfirst(lc)); - update_pg_job(job_name, Anum_pg_job_enable, BoolGetDatum(false)); - } -} - -/* - * @brief reset_job_class - * - * @param gs_job_attribute_rel - * @param disable_job_names - * @param attribute_name - */ -static void reset_job_class(Relation gs_job_attribute_rel, List *disable_job_names, Datum attribute_name, bool force) -{ - if (!force && disable_job_names) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Job class is refered by at least one job."), - errdetail("N/A"), errcause("job class is used"), - erraction("Please pass force true or drop job first"))); - } - - ListCell *lc = NULL; - foreach (lc, disable_job_names) { - Datum job_name = PointerGetDatum(lfirst(lc)); - update_attribute(job_name, attribute_name, CStringGetTextDatum("DEFAULT_JOB_CLASS")); - } -} - -/* - * @brief search_related_jobs - * search all related jobs. - * @param object_name - * @param attribute_name - * @param force - * @return List* - */ -static List *search_related_jobs(Relation gs_job_attribute_rel, Datum object_name, Datum attribute_name, bool force) -{ - List *tuples = search_related_attribute(gs_job_attribute_rel, attribute_name, object_name); - // If force is set to FALSE, a class being dropped must not be referenced by any jobs, otherwise an error occurs. - if (!force && list_length(tuples) != 0) { - HeapTuple tuple = (HeapTuple)linitial(tuples); - bool isNull = false; - Datum related_name = - heap_getattr(tuple, Anum_gs_job_attribute_job_name, RelationGetDescr(gs_job_attribute_rel), &isNull); - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_IN_USE), - errmsg("%s %s refered by job %s", TextDatumGetCString(attribute_name), - TextDatumGetCString(object_name), TextDatumGetCString(related_name)), - errdetail("N/A"), errcause("attribute is used"), - erraction("Please drop object %s", DatumGetPointer(object_name)))); - } - List *disable_job_names = NIL; - ListCell *lc = NULL; - foreach (lc, tuples) { - HeapTuple tuple = (HeapTuple)lfirst(lc); - bool isNull = false; - Datum disable_job_name = - heap_getattr(tuple, Anum_gs_job_attribute_job_name, RelationGetDescr(gs_job_attribute_rel), &isNull); - Assert(!isNull); - disable_job_names = lappend(disable_job_names, DatumGetPointer(PG_DETOAST_DATUM_COPY(disable_job_name))); - } - list_free_deep(tuples); - return disable_job_names; -} - -/* - * @brief drop_inline_program - * Drop inline program if exists. - * @param job_name - */ -void drop_inline_program(const Datum job_name) -{ - Datum attribute_name = CStringGetTextDatum("program_name"); - - Relation rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); - bool isnull = false; - Datum program_name = lookup_job_attribute(rel, job_name, attribute_name, &isnull, true); - heap_close(rel, NoLock); - if (!PointerIsValid(program_name)) { - return; - } - - char *program_name_str = TextDatumGetCString(program_name); - if (strncmp(INLINE_JOB_PROGRAM_PREFIX, program_name_str, strlen(INLINE_JOB_PROGRAM_PREFIX)) == 0) { - delete_from_attribute(program_name); - delete_from_job_proc(program_name); - delete_from_argument(program_name); - } -} - -/* - * @brief drop_single_object_name - * Drop one object, disable all related objects. - * @param object_name - * @param object_type - * @param force - * @param simple when set to true, do not disable relatied objects - */ -static void drop_single_object_name(Datum object_name, const char *object_type, bool force) -{ - check_object_type_matched(object_name, object_type); - delete_from_attribute(object_name); - delete_from_job_proc(object_name); - delete_from_argument(object_name); - - Datum attribute_name; - if (pg_strcasecmp(object_type, "job_class") == 0) { - attribute_name = CStringGetTextDatum("job_class"); - } else if (pg_strcasecmp(object_type, "program") == 0) { - attribute_name = CStringGetTextDatum("program_name"); - } else if (pg_strcasecmp(object_type, "schedule") == 0) { - attribute_name = CStringGetTextDatum("schedule_name"); - } else { - Assert(false); - return; - } - - Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); - List *disable_job_names = search_related_jobs(gs_job_attribute_rel, object_name, attribute_name, force); - disable_related_jobs_force(gs_job_attribute_rel, disable_job_names); - if (pg_strcasecmp(object_type, "job_class") == 0 && list_length(disable_job_names) > 0) { - reset_job_class(gs_job_attribute_rel, disable_job_names, attribute_name, force); - } - heap_close(gs_job_attribute_rel, NoLock); - list_free_deep(disable_job_names); - disable_job_names = NIL; - pfree(DatumGetPointer(attribute_name)); -} - -/* - * @brief drop_single_job_class_internal - * Drop a single job class. - * Note: - * Dropping a job class requires the MANAGE SCHEDULER system privilege. - */ -void drop_single_job_class_internal(PG_FUNCTION_ARGS) -{ - check_object_is_visible(PG_GETARG_DATUM(0), false); - Datum job_class_name = PG_GETARG_DATUM(0); - char *job_class_str = TextDatumGetCString(job_class_name); - if (pg_strcasecmp(job_class_str, "DEFAULT_JOB_CLASS") == 0) { - return; - } - bool force = PG_GETARG_BOOL(1); - drop_single_object_name(job_class_name, "job_class", force); -} - -/* - * @brief drop_single_program_internal - * Drop a single program. - */ -void drop_single_program_internal(PG_FUNCTION_ARGS) -{ - check_object_is_visible(PG_GETARG_DATUM(0), false); - Datum program_name = PG_GETARG_DATUM(0); - - char *program_type_str = get_attribute_value_str(program_name, "program_type", AccessShareLock, true); - if (program_type_str == NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("Cannot find program type of program %s.", TextDatumGetCString(program_name)), - errdetail("Invalid program format."), errcause("N/A"), - erraction("Please check the program name."))); - } - - if (pg_strcasecmp(program_type_str, EXTERNAL_JOB_TYPE) == 0) { - check_privilege(get_role_name_str(), CREATE_EXTERNAL_JOB_PRIVILEGE); - } - pfree_ext(program_type_str); - - bool force = PG_GETARG_BOOL(1); - drop_single_object_name(program_name, "program", force); -} - -/* - * @brief drop_single_schedule_internal - * Drop a single schedule. - */ -void drop_single_schedule_internal(PG_FUNCTION_ARGS) -{ - check_object_is_visible(PG_GETARG_DATUM(0), false); - Datum schedule_name = PG_GETARG_DATUM(0); - bool force = PG_GETARG_BOOL(1); - drop_single_object_name(schedule_name, "schedule", force); -} - -/* - * @brief drop_credential_internal - * Drop a single credential. - */ -void drop_credential_internal(PG_FUNCTION_ARGS) -{ - if (!superuser()) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("Fail to drop credential."), - errdetail("Insufficient privilege to drop credential."), errcause("N/A"), - erraction("Please login in with initial user or contact database administrator."))); - } - const Datum credential_name = PG_GETARG_DATUM(0); - check_object_type_matched(credential_name, "credential"); - bool force = PG_GETARG_BOOL(1); - if (!force) { - Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); - Datum credential_attribute_name = CStringGetTextDatum("credential_name"); - List *tuples = search_related_attribute(gs_job_attribute_rel, credential_attribute_name, credential_name); - pfree(DatumGetPointer(credential_attribute_name)); - credential_attribute_name = Datum(0); - if (list_length(tuples) > 0) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_IN_USE), - errmsg("Fail to drop credential."), - errdetail("Credential %s is refered by jobs.", TextDatumGetCString(credential_name)), - errcause("Credential is in use."), - erraction("Please set force flag to true or drop all associated jobs."))); - } - heap_close(gs_job_attribute_rel, NoLock); - } - delete_from_attribute(credential_name); -} - -/* - * @brief revoke_authorization - * revoke authorization. - * @param username - * @param privilege - * @param skip_disable for drop role only, we do not need to disable anything since all - * objects are dropped by then - */ -static void revoke_authorization(Datum username, Datum privilege, bool skip_disable = false) -{ - Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); - HeapTuple old_tuple = SearchSysCache2(JOBATTRIBUTENAME, username, privilege); - if (old_tuple == NULL) { - heap_close(gs_job_attribute_rel, NoLock); - return; - } - simple_heap_delete(gs_job_attribute_rel, &old_tuple->t_self); - ReleaseSysCache(old_tuple); - heap_close(gs_job_attribute_rel, NoLock); - - if (skip_disable) { - return; - } - - char *privilege_str = TextDatumGetCString(privilege); - char *username_str = TextDatumGetCString(username); - if (pg_strcasecmp(privilege_str, EXECUTE_ANY_PROGRAM_PRIVILEGE) == 0) { - disable_shared_job_from_owner(username_str); - } else if (pg_strcasecmp(privilege_str, RUN_EXTERNAL_JOB_PRIVILEGE) == 0) { - disable_external_job_from_owner(username_str); - } - pfree_ext(username_str); - pfree_ext(privilege_str); -} - -/* - * @brief revoke_user_authorization_internal - * Revoke a user's authorization. - */ -void revoke_user_authorization_internal(PG_FUNCTION_ARGS) -{ - const Datum username = get_role_datum(PG_GETARG_DATUM(0)); - const Datum privilege = PG_GETARG_DATUM(1); - if (!superuser()) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("Fail to revoke authorization."), - errdetail("Insufficient privilege to revoke authorization."), errcause("N/A"), - erraction("Please login in with initial user or contact database administrator."))); - } - check_authorization_valid(privilege); - revoke_authorization(username, privilege); -} - -/* - * @brief lookup_job_attribute - * Look up one attribute. - * @param rel gs_job_attribute relation - * @param object_name Object name(can be any job related objects) - * @param attribute The attribute we looking for - * @param isnull null pointer, mark whether the look up result is null - * @return Datum return the attribute value - */ -Datum lookup_job_attribute(Relation rel, Datum object_name, Datum attribute, bool *isnull, bool miss_ok) -{ - HeapTuple attribute_tuple = SearchSysCache2(JOBATTRIBUTENAME, object_name, attribute); - if (!HeapTupleIsValid(attribute_tuple)) { - if (miss_ok) { - return Datum(0); - } - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("Can not find attribute \'%s\' of object name \'%s\'.", TextDatumGetCString(attribute), - TextDatumGetCString(object_name)), - errdetail("N/A"), errcause("attribute is not exist"), erraction("Please check object name"))); - } - Datum attr = heap_getattr(attribute_tuple, Anum_gs_job_attribute_attribute_value, rel->rd_att, isnull); - Assert((*isnull && !PointerIsValid(attr)) || (!(*isnull) && PointerIsValid(attr))); - Datum attr_cpy = Datum(0); - if (PointerIsValid(attr)) { - attr_cpy = PointerGetDatum(PG_DETOAST_DATUM_COPY(attr)); - } - ReleaseSysCache(attribute_tuple); - - return attr_cpy; -} - -/* - * @brief batch_lookup_job_attribute - * Look up attribute in a batch with given attribute look up names. - * @param attributes Job attributes with attribute name pre-filled - * @param n Number of attributes wanted - */ -void batch_lookup_job_attribute(JobAttribute *attributes, int n) -{ - if (attributes == NULL) { - return; - } - - Relation rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); - for (int i = 0; i < n; i++) { - attributes[i].value = lookup_job_attribute(rel, attributes[i].object_name, attributes[i].name, - &attributes[i].null, false); - } - heap_close(rel, NoLock); -} - -/* - * @brief lookup_program_argument - * Look up one program argument. - * @param rel gs_job_argument relation - * @param job_name Job name - * @param program_name Program name - * @param position Position of the argument - * @param argument Job argument context - */ -void lookup_program_argument(Relation rel, Datum job_name, Datum program_name, int position, JobArgument *argument) -{ - HeapTuple argument_tuple = SearchSysCache2(JOBARGUMENTPOSITION, job_name, Int32GetDatum(position)); - if (!HeapTupleIsValid(argument_tuple)) { - argument_tuple = SearchSysCache2(JOBARGUMENTPOSITION, program_name, Int32GetDatum(position)); - if (!HeapTupleIsValid(argument_tuple)) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("Can not find argument info of object \'%s\'.", TextDatumGetCString(job_name)), - errdetail("N/A"), errcause("argument information not found"), - erraction("Please check object name"))); - } - } - bool isnull = true; - /* Name: type */ - Datum type = heap_getattr(argument_tuple, Anum_gs_job_argument_argument_type, rel->rd_att, &isnull); - if (!isnull) { - argument->argument_type = pstrdup((char *)type); - } else { - argument->argument_type = NULL; - } - - /* Datum: argument_value */ - Datum argument_value = heap_getattr(argument_tuple, Anum_gs_job_argument_argument_value, rel->rd_att, &isnull); - if (!isnull) { - argument->argument_value = TextDatumGetCString(argument_value); - } else { - argument->argument_value = NULL; - } - - if (argument->argument_value != NULL) { - ReleaseSysCache(argument_tuple); - return; - } - - /* Datum: default_value */ - Datum default_value = heap_getattr(argument_tuple, Anum_gs_job_argument_default_value, rel->rd_att, &isnull); - if (!isnull) { - argument->argument_value = TextDatumGetCString(default_value); - } else { - argument->argument_value = NULL; - } - - if (argument->argument_value == NULL) { - ReleaseSysCache(argument_tuple); - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("Cannot get argument value of job %s.", TextDatumGetCString(job_name)), - errdetail("N/A"), errcause("argument information not found"), - erraction("Please check object name"))); - } - ReleaseSysCache(argument_tuple); -} - -/* - * @brief batch_lookup_program_argument - * Allow us to look up multiple arguments(all the available arguments) in a batch. - * @param job_name Job name - * @param program_name Program name - * @param number_of_arguments Number of arguments - */ -JobArgument *batch_lookup_program_argument(Datum job_name, Datum program_name, int number_of_arguments) -{ - if (number_of_arguments <= 0) { - return NULL; - } - JobArgument *arguments = (JobArgument *)palloc0(number_of_arguments * sizeof(JobArgument)); - Relation rel = heap_open(GsJobArgumentRelationId, AccessShareLock); - for (int i = 0; i < number_of_arguments; i++) { - lookup_program_argument(rel, job_name, program_name, i + 1, arguments + i); - if (arguments[i].argument_value == NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("Can not find argument %d in system table gs_job_argument.", i + 1), - errdetail("N/A"), errcause("argument %d is not defined", i + 1), - erraction("Please check program arguments"))); - } - } - heap_close(rel, NoLock); - return arguments; -} - -/* - * @brief lookup_credential_username - * Look up credential username. - * @param credential_name - * @return char* - */ -char *lookup_credential_username(Datum job_name) -{ - bool use_default = false; - char *job_type_str = get_job_type(job_name, false); - bool is_shell_job = pg_strcasecmp(job_type_str, EXTERNAL_JOB_TYPE) == 0; - pfree_ext(job_type_str); - if (!is_shell_job) { - return NULL; - } - Datum credential_name = get_attribute_value(job_name, "credential_name", AccessShareLock, true, true); - if (credential_name == Datum(0)) { - use_default = true; - credential_name = CStringGetTextDatum(DEFAULT_CREDENTIAL_NAME); - } - char *attribute_value_str = get_attribute_value_str(credential_name, "object_type", AccessShareLock, true, true); - if (attribute_value_str == NULL || pg_strcasecmp(attribute_value_str, "credential") != 0) { - if (use_default) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("No database wise credential found."), - errdetail("Need to create default credential for database with name 'db_credential'"), - errcause("No credential found."), - erraction("Please create a default or custom credential."))); - return NULL; /* compiler happy */ - } - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("Fail to look up credential."), - errdetail("Credential specified does not exist."), - errcause("No credential found."), - erraction("Please check credential name."))); - } - pfree_ext(attribute_value_str); - char *username = get_attribute_value_str(credential_name, "username", AccessShareLock, false, false); - return username; -} - -/* - * @brief lookup_job_value - * Look up job values. - * @param job_name - * @return JobValue* - */ -JobValue *lookup_job_value(Datum job_name) -{ - Relation pg_job_rel = heap_open(PgJobRelationId, AccessShareLock); - HeapTuple tuple = search_from_pg_job(pg_job_rel, job_name); - if (!HeapTupleIsValid(tuple)) { - heap_close(pg_job_rel, AccessShareLock); - return NULL; - } - JobValue *job_value = (JobValue *)palloc0(sizeof(JobValue)); - bool isnull = false; - - job_value->end_date = DatumGetTimestamp(heap_getattr(tuple, Anum_pg_job_end_date, pg_job_rel->rd_att, &isnull)); - if (isnull) { - job_value->end_date = get_scheduler_max_timestamp(); - } - job_value->nspname = DatumGetPointer(heap_getattr(tuple, Anum_pg_job_nspname, pg_job_rel->rd_att, &isnull)); - if (isnull) { - job_value->nspname = NULL; - } else { - job_value->nspname = pstrdup(job_value->nspname); - } - job_value->interval = TextDatumGetCString(heap_getattr(tuple, Anum_pg_job_interval, pg_job_rel->rd_att, &isnull)); - if (isnull || pg_strcasecmp(job_value->interval, "null") == 0) { - pfree(job_value->interval); - job_value->interval = NULL; - } - job_value->fail_count = DatumGetInt16(heap_getattr(tuple, Anum_pg_job_failure_count, pg_job_rel->rd_att, &isnull)); - if (isnull) { - job_value->fail_count = 0; - } - heap_close(pg_job_rel, AccessShareLock); - return job_value; -} - -/* - * @brief make_job_proc_value - * Context struct initialization. - * @param job_action Job action datum - * @return JobProcValue* Out context - */ -JobProcValue *make_job_proc_value(Datum job_action) -{ - JobProcValue *job_proc_value = (JobProcValue *)palloc0(sizeof(JobProcValue)); - job_proc_value->action = job_action; - job_proc_value->action_str = TextDatumGetCString(job_action); - return job_proc_value; -} - -/* - * @brief make_job_attribute_value - * Context struct initialization. - * @param attributes Attribute keys - * @return JobAttributeValue* Out context - */ -JobAttributeValue *make_job_attribute_value(JobAttribute *attributes) -{ - enum {PROGRAM_TYPE = 0, PROGRAM_ENABLED, NUM_OF_ARGS, AUTO_DROP, JOB_CLASS}; - JobAttributeValue *job_attribute_value = (JobAttributeValue *)palloc0(sizeof(JobAttributeValue)); - job_attribute_value->job_type = attributes[PROGRAM_TYPE].value; - attributes[PROGRAM_TYPE].value = Datum(0); - job_attribute_value->program_enable = TextToBool(attributes[PROGRAM_ENABLED].value); - job_attribute_value->auto_drop = TextToBool(attributes[AUTO_DROP].value); - job_attribute_value->number_of_arguments = TextToInt32(attributes[NUM_OF_ARGS].value); - job_attribute_value->job_class = attributes[JOB_CLASS].value; - - return job_attribute_value; -} - -/* - * @brief make_job_target - * Make the job target by put attributes into a well organized structure. - * @param job_name Job name - * @param program_name Associated program name(can be a inlined program) - * @param attributes The job attributes - * @return JobTarget* The structure created - */ -static JobTarget *make_job_target(Datum job_name, Datum program_name, JobAttribute *attributes) -{ - Datum job_id; - Datum job_action; - - /* Lookup id, action attributes */ - lookup_pg_job_proc(job_name, &job_id, &job_action); - if (job_action == Datum(0)) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("job %s's action is undefined.", TextDatumGetCString(job_name)), - errdetail("N/A"), errcause("job's action is undefined"), erraction("Please check job_name"))); - } - - /* Fill up job targets */ - JobTarget *job_target = (JobTarget *)palloc0(sizeof(JobTarget)); - job_target->job_name = job_name; - job_target->id = job_id; - job_target->job_proc_value = make_job_proc_value(job_action); - job_target->job_attribute_value = make_job_attribute_value(attributes); - job_target->job_attribute_value->username = lookup_credential_username(job_name); - job_target->job_attribute_value->program_name = program_name; - job_target->arguments = batch_lookup_program_argument(job_name, program_name, - job_target->job_attribute_value->number_of_arguments); - job_target->job_value = lookup_job_value(job_name); - return job_target; -} - -/* - * @brief get_job_target - * Get the Job Target object. - * @param job_name Job name datum - * @return JobTarget* Out context - */ -static JobTarget *get_job_target(Datum job_name) -{ - Datum program_name = get_attribute_value(job_name, "program_name", AccessShareLock); - - /* Lookup corresponding attributes */ - - const char *attribute_names[] = {"program_type", "enabled", "number_of_arguments", "auto_drop", "job_class"}; - const Datum object_names[] = {program_name, program_name, program_name, job_name, job_name}; - int count = lengthof(attribute_names); - JobAttribute *lookup_attributes = (JobAttribute *)palloc0(sizeof(JobAttribute) * count); - for (int i = 0; i < count; i++) { - lookup_attributes[i].object_name = object_names[i]; - lookup_attributes[i].name = CStringGetTextDatum(attribute_names[i]); - } - batch_lookup_job_attribute(lookup_attributes, count); - - /* Job prep */ - JobTarget *job_target = make_job_target(job_name, program_name, lookup_attributes); - for (int i = 0; i < count; i++) { - lookup_attributes[i].object_name = object_names[i]; - pfree(DatumGetPointer(lookup_attributes[i].name)); - if (lookup_attributes[i].value != Datum(0)) { - pfree(DatumGetPointer(lookup_attributes[i].value)); - } - } - pfree_ext(lookup_attributes); - return job_target; -} - -/* - * @brief check_program_type_argument - * Perform a simple check on program type argument. - * @param program_type - * @param number_of_arguments - */ -void check_program_type_argument(Datum program_type, int number_of_arguments) -{ - if (pg_strcasecmp(TextDatumGetCString(program_type), PLSQL_JOB_TYPE) == 0 && number_of_arguments != 0) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Invalid program type or argument"), - errdetail("Program type PLSQL_BLOCK must has no argument"), - errcause("Program type is PLSQL_BLOCK, so the number of arguments must be 0"), - erraction("Please check program type or argument param"))); - } - const int max_arguments = 255; - if (number_of_arguments > max_arguments || number_of_arguments < 0) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Invalid program arguments"), - errdetail("Program arguments must less euqal than 255 and greater euqal than zero"), - errcause("Program arguments must less euqal than 255"), - erraction("Please check program argument param"))); - } -} - -/* - * @brief check_str_valid - * Check if a user input string contains danger characters. - * @param str - */ -static char *replace_all_danger_character(const char *str) -{ - if (strstr(str, "\\\"") != NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_NAME), - errmsg("Fail to parse script parameter."), - errdetail("Parameter contains escaped double quotes '\\\"'."), errcause("Command is invalid"), - erraction("Please enter a valid command"))); - } - StringInfoData buffer; - initStringInfo(&buffer); - appendStringInfoString(&buffer, "\\\""); /* \" for start of param */ - int len = strlen(str); - for (int i = 0; i < len; i++) { - if (str[i] == '"') { - appendStringInfoString(&buffer, "\\\\\\\""); /* \\\" for escaping the escaped double quote */ - } else if (str[i] == '\\' || str[i] == '|') { - appendStringInfoString(&buffer, "\\"); /* \ for escaping other dangerous str[i] character */ - appendStringInfoChar(&buffer, str[i]); - } else { - appendStringInfoChar(&buffer, str[i]); - } - } - appendStringInfoString(&buffer, "\\\""); /* \" */ - return buffer.data; -} - -/* - * @brief check_real_path_valid - * Check if a user input string is a valid real path. - * @param file_name - */ -static void check_real_path_valid(const Datum file_name) -{ - char *command = TextDatumGetCString(file_name); - char path_name_arr[PATH_MAX + 1] = {0}; - char *path_name = realpath(command, path_name_arr); - if (path_name == NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_NAME), - errmsg("Fail to execute external job."), - errdetail("Cannot execute external script with given path."), errcause("Command is invalid"), - erraction("Please enter a valid command"))); - } - const char *danger_character_list[] = {"|", ";", "&", "$", "<", ">", "`", "\\", "'", "\"", "{", - "}", "(", ")", "[", "]", "~", "*", "?", "!", "\n", NULL}; - for (int i = 0; danger_character_list[i] != NULL; i++) { - if (strstr(path_name, danger_character_list[i]) != NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_NAME), - errmsg("invalid token \"%s\"", danger_character_list[i]), - errdetail("str contains invalid character"), errcause("str is invalid"), - erraction("Please enter a valid str"))); - } - } - pfree(command); -} - -/* - * @brief run_backend_job_internal - * Runs a job immediately by reset job_status and next_run_time. - * Note: use_current_session is not functioning. - */ -void run_backend_job_internal(PG_FUNCTION_ARGS) -{ - check_object_is_visible(PG_GETARG_DATUM(0)); - Datum job_name = PG_GETARG_DATUM(0); - check_object_type_matched(job_name, "job"); - if (is_job_running(job_name)) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_STATUS), - errmsg("Job is still running."), - errdetail("Cannot run job when job is already running."), - errcause("Job is still running."), - erraction("Please try again later."))); - } - JobTarget *job_target = get_job_target(job_name); /* for check only */ - pfree_ext(job_target); - - /* Run job */ - Datum current_time = DirectFunctionCall1(timestamptz_timestamp, TimestampTzGetDatum(GetCurrentTimestamp())); - Datum job_related_attr[] = {current_time, CharGetDatum(PGJOB_SUCC_STATUS)}; - int job_related_num[] = {Anum_pg_job_next_run_date, Anum_pg_job_job_status}; - bool isnull[] = {false, false}; - update_pg_job_multi_columns(job_name, (int *)job_related_num, job_related_attr, isnull, lengthof(job_related_attr)); -} - -/* - * @brief check_sql_job_ready - * Check if job is ready to run. - * @param job_target - */ -static void check_sql_job_ready(JobTarget *job_target) -{ - if (!job_target->job_attribute_value->program_enable) { - StartTransactionCommand(); - update_pg_job(job_target->job_name, Anum_pg_job_enable, BoolGetDatum(false)); - CommitTransactionCommand(); - char *program_name_str = TextDatumGetCString(job_target->job_attribute_value->program_name); - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_STATUS), - errmsg("program %s of job %s is disabled.", - program_name_str, TextDatumGetCString(job_target->job_name)), - errdetail("N/A"), errcause("program of job is disabled"), - erraction("Please enable program %s of job first", program_name_str))); - } - check_program_type_argument(job_target->job_attribute_value->job_type, - job_target->job_attribute_value->number_of_arguments); - check_object_is_visible(job_target->job_attribute_value->program_name); -} - -/* - * @brief check_external_job_ready - * - * @param job_target - */ -static void check_external_job_ready(JobTarget *job_target) -{ - check_sql_job_ready(job_target); - check_real_path_valid(PointerGetDatum(job_target->job_proc_value->action)); - - for (int i = 0; i < job_target->job_attribute_value->number_of_arguments; i++) { - job_target->arguments[i].argument_value = replace_all_danger_character(job_target->arguments[i].argument_value); - } - if (job_target->job_attribute_value->username == NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("job %s's credential username is undefined.", - TextDatumGetCString(job_target->job_name)), - errdetail("N/A"), errcause("credential username is undefined"), - erraction("Please check job_name"))); - } - check_privilege(get_role_name_str(), RUN_EXTERNAL_JOB_PRIVILEGE); -} - -/* - * @brief expire_backend_job - * Disable or drop job when job expired. - */ -void expire_backend_job(Datum job_name, bool auto_drop) -{ - start_xact_command(); - if (auto_drop) { - drop_inline_program(job_name); - delete_from_attribute(job_name); - delete_from_job_proc(job_name); - delete_from_argument(job_name); - delete_from_job(job_name); - } else { - update_pg_job(job_name, Anum_pg_job_enable, BoolGetDatum(false)); - } - finish_xact_command(); -} - -/* - * @brief refresh_backend_job_status - */ -static bool refresh_backend_job_status(JobTarget *job_target, bool after_run) -{ - /* after run */ - if (after_run && job_target->job_value->interval == NULL) { - expire_backend_job(job_target->job_name, job_target->job_attribute_value->auto_drop); - return true; - } - - /* before run */ - TimestampTz cur_timestamp = GetCurrentTimestamp(); - bool expire = DatumGetBool(DirectFunctionCall2(timestamptz_gt_timestamp, cur_timestamp, - job_target->job_value->end_date)); - if (expire) { - expire_backend_job(job_target->job_name, job_target->job_attribute_value->auto_drop); - return true; - } - return false; -} - -/* - * @brief run_job_internal - * Runs a job immediately, only when external job, others ereport error - * Note: use_current_session is not functioning. - */ -Datum run_foreground_job_internal(PG_FUNCTION_ARGS) -{ - check_object_is_visible(PG_GETARG_DATUM(0)); - Datum job_name = PG_GETARG_DATUM(0); - check_object_type_matched(job_name, "job"); - char *program_type = get_job_type(job_name, false); - if (pg_strcasecmp(program_type, EXTERNAL_JOB_TYPE) != 0) { // shell - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("when run current session job, program_type %s is not supported.", program_type), - errdetail("program_type not supported"), errcause("N/A"), - erraction("please check program type"))); - } - pfree(program_type); - char *res = run_external_job(job_name); - Assert(res != NULL); - Datum res_text = CStringGetTextDatum(res); - pfree_ext(res); - return res_text; -} - -/* - * @brief run_sql_job - * Run sql job. - * @param job_name - * @param buf - * @return true - * @return false - */ -static bool run_sql_job(Datum job_name, StringInfoData *buf) -{ - JobTarget *job_target = get_job_target(job_name); - check_sql_job_ready(job_target); - if (t_thrd.role == JOB_WORKER) { - if (refresh_backend_job_status(job_target, false)) { - return true; - } - } - if (buf->data[0] == '\0' && job_target->job_value->nspname != NULL) { - appendStringInfo(buf, "set current_schema=%s;", quote_identifier(job_target->job_value->nspname)); - } - appendStringInfo(buf, "%s", job_target->job_proc_value->action_str); - execute_simple_query(buf->data); - if (t_thrd.role == JOB_WORKER) { - (void)refresh_backend_job_status(job_target, true); - } - return true; -} - -/* - * @brief run_procedure_job - * Run stored procedure job. - * @param job_name - * @param buf - * @return true - * @return false - */ -static bool run_procedure_job(Datum job_name, StringInfoData *buf) -{ - JobTarget *job_target = get_job_target(job_name); - check_sql_job_ready(job_target); - if (t_thrd.role == JOB_WORKER) { - if (refresh_backend_job_status(job_target, false)) { - return true; - } - } - if (buf->data[0] == '\0' && job_target->job_value->nspname != NULL) { - appendStringInfo(buf, "set current_schema=%s;", quote_identifier(job_target->job_value->nspname)); - } - appendStringInfoString(buf, "call "); - appendStringInfoString(buf, job_target->job_proc_value->action_str); - appendStringInfoString(buf, "("); - for (int i = 0; i < job_target->job_attribute_value->number_of_arguments; i++) { - if (i > 0) { - appendStringInfoString(buf, ", "); - } - appendStringInfoString(buf, "'"); - appendStringInfoString(buf, job_target->arguments[i].argument_value); - appendStringInfoString(buf, "'::"); - appendStringInfoString(buf, job_target->arguments[i].argument_type); - } - appendStringInfoString(buf, ");"); - execute_simple_query(buf->data); - if (t_thrd.role == JOB_WORKER) { - (void)refresh_backend_job_status(job_target, true); - } - return true; -} - -/* - * @brief ssh_run_external_program - * Create a SSH connection string for execute a program - * @param username - * @param action Program action - * @return char* - */ -static char *ssh_run_external_program(const char *username, const char *action) -{ - StringInfoData ssh_buf; - initStringInfo(&ssh_buf); - appendStringInfoString(&ssh_buf, "ssh "); - appendStringInfoString(&ssh_buf, username); - appendStringInfoString(&ssh_buf, "@localhost \""); - appendStringInfoString(&ssh_buf, action); - appendStringInfoString(&ssh_buf, "\""); - appendStringInfoString(&ssh_buf, " 2>&1"); - - FILE *ssh_stream = popen(ssh_buf.data, "r"); - if (ssh_stream == NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), - errmsg("ssh to username failed"), errdetail("cannot ssh to username, popen fail"), - errcause("N/A"), - erraction("Please append ~/.ssh/id_rsa.pub to username's homepath/.ssh/authorized_keys"))); - } - StringInfoData res_buf; - initStringInfo(&res_buf); - const int bufferLen = 1024; - char buffer[bufferLen]; - while (fgets(buffer, sizeof(buffer), ssh_stream) != NULL) { - buffer[bufferLen - 1] = '\0'; - appendStringInfoString(&res_buf, buffer); - } - pclose(ssh_stream); - pfree(ssh_buf.data); - return res_buf.data; -} - -/* - * @brief run_external_job - * @param job_name - */ -static char *run_external_job(Datum job_name) -{ - JobTarget *job_target = get_job_target(job_name); - Assert(job_target->job_proc_value->action != Datum(0)); - check_external_job_ready(job_target); - if (t_thrd.role == JOB_WORKER) { - if (refresh_backend_job_status(job_target, false)) { - return NULL; - } - } - StringInfoData action_buf; - initStringInfo(&action_buf); - appendStringInfoString(&action_buf, job_target->job_proc_value->action_str); - for (int i = 0; i < job_target->job_attribute_value->number_of_arguments; i++) { - appendStringInfoString(&action_buf, " "); - appendStringInfoString(&action_buf, job_target->arguments[i].argument_value); - } - char *res = ssh_run_external_program(job_target->job_attribute_value->username, action_buf.data); - if (t_thrd.role == JOB_WORKER) { - (void)refresh_backend_job_status(job_target, true); - } - return res; -} - -/* - * @brief execute_backend_scheduler_job - * Execute job main. - */ -bool execute_backend_scheduler_job(Datum job_name, StringInfoData *buf) -{ - if (job_name == Datum(0)) { - return false; - } - char *program_type = get_job_type(job_name, true); - if (!PointerIsValid(program_type)) { - return false; - } - if (pg_strcasecmp(program_type, PLSQL_JOB_TYPE) == 0) { - return run_sql_job(job_name, buf); - } - if (pg_strcasecmp(program_type, PRECEDURE_JOB_TYPE) == 0) { - return run_procedure_job(job_name, buf); - } - if (pg_strcasecmp(program_type, EXTERNAL_JOB_TYPE) != 0) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("program_type %s cannot be recognized.", program_type), - errdetail("program_type crashed"), errcause("N/A"), - erraction("please check program type"))); - } - pfree(program_type); - (void)run_external_job(job_name); - return true; -} - -/* - * @brief stop_single_job_force - * Send a SIGINT/SIGTERM to job worker. - * @param job_name - * @param force - */ -static void stop_single_job_force(Datum job_name, bool force) -{ - Relation pg_job_rel = heap_open(PgJobRelationId, RowExclusiveLock); - HeapTuple tuple = search_from_pg_job(pg_job_rel, job_name); - if (!HeapTupleIsValid(tuple)) { - heap_close(pg_job_rel, NoLock); - return; - } - Form_pg_job pg_job_value = (Form_pg_job)GETSTRUCT(tuple); - if (pg_job_value->job_status != PGJOB_RUN_STATUS || pg_job_value->current_postgres_pid == -1) { - heap_close(pg_job_rel, NoLock); - heap_freetuple_ext(tuple); - return; - } - if (!force) { - gs_signal_send(pg_job_value->current_postgres_pid, SIGINT); - } else { - gs_signal_send(pg_job_value->current_postgres_pid, SIGTERM); - } - - heap_close(pg_job_rel, NoLock); - heap_freetuple_ext(tuple); -} - -/* - * @brief stop_job_by_job_class_force - * Stop jobs by its job class. - * @param job_class_name - * @param force - */ -static void stop_job_by_job_class_force(Datum job_class_name, bool force) -{ - Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); - Datum job_class_attribute_name = CStringGetTextDatum("job_class"); - List *tuples = search_related_attribute(gs_job_attribute_rel, job_class_attribute_name, job_class_name); - pfree(DatumGetPointer(job_class_attribute_name)); - job_class_attribute_name = Datum(0); - if (list_length(tuples) == 0) { - heap_close(gs_job_attribute_rel, NoLock); - return; - } - ListCell *lc = NULL; - foreach(lc, tuples) { - HeapTuple tuple = (HeapTuple)lfirst(lc); - bool isnull = false; - Datum job_name = heap_getattr(tuple, Anum_gs_job_attribute_job_name, gs_job_attribute_rel->rd_att, &isnull); - stop_single_job_force(job_name, force); - } - heap_close(gs_job_attribute_rel, NoLock); - list_free_deep(tuples); -} - -/* - * @brief stop_single_job_internal - * Stop the job by sending SIGINT/SIGTERM signals to job worker. - */ -void stop_single_job_internal(PG_FUNCTION_ARGS) -{ - Datum job_name = PG_GETARG_DATUM(0); - bool force = PG_GETARG_BOOL(1); - char *object_type = get_attribute_value_str(job_name, "object_type", RowExclusiveLock, true); - if (object_type == NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("Undefined object %s.", TextDatumGetCString(job_name)), - errdetail("Job or job class %s does not exist", TextDatumGetCString(job_name)), - errcause("N/A"), erraction("Please check the job name."))); - } - - if (pg_strcasecmp(object_type, "job") == 0) { - stop_single_job_force(job_name, force); - } else if (pg_strcasecmp(object_type, "job_class") == 0) { - stop_job_by_job_class_force(job_name, force); - } else { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("object_type can only be job or job_class."))); - } -} - -/* - * @brief drop_single_job_name - * Drop a single job with specified job name. - * @param job_name - * @param defer - * @param force - */ -static void drop_single_job_name(Datum job_name, bool defer, bool force) -{ - check_object_type_matched(job_name, "job"); - char *job_type = get_job_type(job_name, true); - if (job_type == NULL || pg_strcasecmp(job_type, EXTERNAL_JOB_TYPE) == 0) { - check_privilege(get_role_name_str(), CREATE_EXTERNAL_JOB_PRIVILEGE); - } - pfree_ext(job_type); - - if (force && defer) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_STATUS), - errmsg("Cannot defer drop_job when 'force' is on."), - errdetail("N/A"), errcause("Conflicting condition."), - erraction("Please recheck the parameters."))); - } - bool is_running = is_job_running(job_name); - if (is_running && defer) { - /* if defer is set, expire the job immediately */ - Datum attribute_name_1 = CStringGetTextDatum("auto_drop"); - Datum attribute_value_1 = BoolToText(true); - set_job_attribute(job_name, attribute_name_1, attribute_value_1); - Datum attribute_name_2 = CStringGetTextDatum("end_date"); - Datum attribute_value_2 = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); - attribute_value_2 = DirectFunctionCall1(timestamp_text, attribute_value_2); - set_job_attribute(job_name, attribute_name_2, attribute_value_2); - return; - } - if (is_running && !force) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_STATUS), - errmsg("Cannot drop job %s when job is running.", TextDatumGetCString(job_name)), - errdetail("Job is running."), errcause("N/A"), - erraction("Please check the job status."))); - } - if (is_running && force) { - DirectFunctionCall2((PGFunction)stop_single_job_internal, job_name, BoolGetDatum(true)); - } - drop_inline_program(job_name); - delete_from_attribute(job_name); - delete_from_job_proc(job_name); - delete_from_argument(job_name); - delete_from_job(job_name); -} - -/* - * @brief drop_scheduler_jobs_from_class - * Drop all jobs with specified job class. - */ -void drop_scheduler_jobs_from_class(Datum job_class_name, bool defer, bool force) -{ - Datum attribute_name = CStringGetTextDatum("job_class"); - Relation rel = heap_open(GsJobAttributeRelationId, AccessShareLock); - List *tuples = search_related_attribute(rel, attribute_name, job_class_name); - List *drop_job_names = NIL; - ListCell *lc = NULL; - foreach(lc, tuples) { - HeapTuple tuple = (HeapTuple)lfirst(lc); - bool isnull = false; - Datum job_name = heap_getattr(tuple, Anum_gs_job_attribute_job_name, rel->rd_att, &isnull); - Assert(!isnull); - drop_job_names = lappend(drop_job_names, DatumGetPointer(PG_DETOAST_DATUM_COPY(job_name))); - } - heap_close(rel, AccessShareLock); - - /* call disable */ - lc = NULL; - foreach(lc, drop_job_names) { - Datum job_name = (Datum)lfirst(lc); - check_object_is_visible(job_name, false); - drop_single_job_name(job_name, defer, force); - } -} - -/* - * @brief drop_job_internal - * Drop the job and all relative objects(inline program etc.) - * Note: defer and commit_semantic has no purpose yet. - */ -void drop_single_job_internal(PG_FUNCTION_ARGS) -{ - check_object_is_visible(PG_GETARG_DATUM(0), false); - Datum job_name = PG_GETARG_DATUM(0); - bool force = PG_GETARG_BOOL(1); - bool defer = PG_GETARG_BOOL(2); - - char *object_type = get_attribute_value_str(job_name, "object_type", RowExclusiveLock, true); - if (object_type == NULL) { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("Undefined object %s.", TextDatumGetCString(job_name)), - errdetail("Job or job class %s does not exist", TextDatumGetCString(job_name)), - errcause("N/A"), erraction("Please check the job name."))); - } - - if (pg_strcasecmp(object_type, "job") == 0) { - drop_single_job_name(job_name, defer, force); - } else if (pg_strcasecmp(object_type, "job_class") == 0) { - drop_scheduler_jobs_from_class(job_name, defer, force); - } else { - ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("name %s is %s.", TextDatumGetCString(job_name), object_type), - errdetail("N/A"), errcause("name has wrong type"), - erraction("Please check %s name", TextDatumGetCString(job_name)))); - } - return; -} - -/* - * @brief is_private_job - * Is a privatejob or not? job use log_user instead of owner, so we made a seperate function. - * @param job_name - * @param user_str - */ -static bool is_private_job(Datum job_name, const char *user_str) -{ - bool isnull = false; - Relation pg_job_rel = heap_open(PgJobRelationId, AccessShareLock); - HeapTuple tuple = search_from_pg_job(pg_job_rel, job_name); - if (!HeapTupleIsValid(tuple)) { - heap_close(pg_job_rel, AccessShareLock); - return false; - } - Datum creator = heap_getattr(tuple, Anum_pg_job_log_user, pg_job_rel->rd_att, &isnull); - char *creator_str = DatumGetCString(creator); - if (pg_strcasecmp(creator_str, user_str) == 0) { - heap_close(pg_job_rel, AccessShareLock); - return true; - } - heap_close(pg_job_rel, AccessShareLock); - return false; -} - -/* - * @brief is_private_scheduler_object - * Check if an object is fully owned by given user. - * @param rel gs_job_attribute relation - * @param object_name object name - * @param user_str given user name - * @return true is fully owned - * @return false not fully owned - */ -bool is_private_scheduler_object(Relation rel, Datum object_name, const char *user_str) -{ - bool isnull = true; - Datum attr = lookup_job_attribute(rel, object_name, CStringGetTextDatum("object_type"), &isnull, false); - char *attr_str = TextDatumGetCString(attr); - if (pg_strcasecmp(attr_str, "job") == 0) { - pfree_ext(attr_str); - if (!is_private_job(object_name, user_str)) { - return false; - } - Datum program_name = \ - lookup_job_attribute(rel, object_name, CStringGetTextDatum("program_name"), &isnull, false); - if (is_private_scheduler_object(rel, program_name, user_str)) { - return true; /* program is private */ - } - return false; - } - pfree_ext(attr_str); - Datum owner = lookup_job_attribute(rel, object_name, CStringGetTextDatum("owner"), &isnull, true); - if (owner == Datum(0)) { - /* credential does not have owner */ - return true; - } - char *owner_str = TextDatumGetCString(owner); - if (pg_strcasecmp(owner_str, user_str) != 0) { - pfree_ext(owner_str); - return false; - } - pfree_ext(owner_str); - return true; -} - -/* - * @brief disable_job_from_owner - * Disable all shared objects base on its owner. - * @param user_str usually current_user - */ -void disable_shared_job_from_owner(const char *user_str) -{ - Datum attribute_name = CStringGetTextDatum("owner"); - Datum attribute_value = CStringGetTextDatum(user_str); - Relation rel = heap_open(GsJobAttributeRelationId, AccessShareLock); - List *tuples = search_related_attribute(rel, attribute_name, attribute_value); - List *disable_job_names = NIL; - ListCell *lc = NULL; - foreach(lc, tuples) { - HeapTuple tuple = (HeapTuple)lfirst(lc); - bool isnull = false; - Datum object_name = heap_getattr(tuple, Anum_gs_job_attribute_job_name, rel->rd_att, &isnull); - Assert(!isnull); - if (is_private_scheduler_object(rel, object_name, user_str)) { - continue; - } - /* here should be all jobs, which its program is not private */ - disable_job_names = lappend(disable_job_names, DatumGetPointer(PG_DETOAST_DATUM_COPY(object_name))); - } - heap_close(rel, AccessShareLock); - - /* call disable */ - lc = NULL; - foreach(lc, disable_job_names) { - Datum program_name = (Datum)lfirst(lc); - Datum enable_value = BoolToText(false); - enable_single_force(program_name, enable_value, true); - } - list_free_deep(disable_job_names); -} - -/* - * @brief is_internal_scheduler_object - * Is object an internel object? - * @param rel - * @param object_name - * @param user_str - */ -bool is_internal_scheduler_object(Relation rel, Datum object_name) -{ - bool isnull = true; - Datum attr = lookup_job_attribute(rel, object_name, CStringGetTextDatum("object_type"), &isnull, false); - char *attr_str = TextDatumGetCString(attr); - if (pg_strcasecmp(attr_str, "job") == 0) { - pfree_ext(attr_str); - Datum program_name = \ - lookup_job_attribute(rel, object_name, CStringGetTextDatum("program_name"), &isnull, false); - if (is_internal_scheduler_object(rel, program_name)) { - return true; /* program is internal */ - } - return false; - } - pfree_ext(attr_str); - Datum program_type = lookup_job_attribute(rel, object_name, CStringGetTextDatum("program_type"), &isnull, true); - if (program_type == Datum(0)) { - return true; - } - char *program_type_str = TextDatumGetCString(program_type); - if (pg_strcasecmp(program_type_str, EXTERNAL_JOB_TYPE) == 0) { - pfree_ext(program_type_str); - return false; - } - pfree_ext(program_type_str); - return true; -} - -/* - * @brief disable_external_job_from_owner - * Disable all external objects base on its owner. - * @param user_str usually current_user - */ -void disable_external_job_from_owner(const char *user_str) -{ - Datum attribute_name = CStringGetTextDatum("owner"); - Datum attribute_value = CStringGetTextDatum(user_str); - Relation rel = heap_open(GsJobAttributeRelationId, AccessShareLock); - List *tuples = search_related_attribute(rel, attribute_name, attribute_value); - List *disable_object_names = NIL; - ListCell *lc = NULL; - foreach(lc, tuples) { - HeapTuple tuple = (HeapTuple)lfirst(lc); - bool isnull = false; - Datum object_name = heap_getattr(tuple, Anum_gs_job_attribute_job_name, rel->rd_att, &isnull); - Assert(!isnull); - if (is_internal_scheduler_object(rel, object_name)) { - continue; - } - Datum attr = lookup_job_attribute(rel, object_name, CStringGetTextDatum("object_type"), &isnull, false); - char *attr_str = TextDatumGetCString(attr); - if (pg_strcasecmp(attr_str, "job") != 0) { - pfree_ext(attr_str); - continue; /* we skip non-job for now */ - } - /* here should be a job with external program */ - disable_object_names = lappend(disable_object_names, DatumGetPointer(PG_DETOAST_DATUM_COPY(object_name))); - } - heap_close(rel, AccessShareLock); - - /* call disable */ - lc = NULL; - foreach(lc, disable_object_names) { - Datum program_name = (Datum)lfirst(lc); - Datum enable_value = BoolToText(false); - enable_single_force(program_name, enable_value, true); - } - list_free_deep(disable_object_names); -} - -/* - * @brief disable_related_job_with_program - * Disable all related jobs created by users other than 'user_str' with a given program. - * @param program_name - * @param type_str - * @param user_str - */ -static void disable_related_job_with_program(Datum program_name, const char *type_str, const char *user_str = NULL) -{ - if (pg_strcasecmp(type_str, "program") != 0) { - return; - } - - ListCell *lc = NULL; - Datum attribute_name = CStringGetTextDatum("program_name"); - Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, AccessShareLock); - List *disable_job_names = search_related_jobs(gs_job_attribute_rel, program_name, attribute_name, true); - heap_close(gs_job_attribute_rel, AccessShareLock); - - foreach (lc, disable_job_names) { - Datum job_name = PointerGetDatum(lfirst(lc)); - char *owner = get_attribute_value_str(job_name, "owner", AccessShareLock, false, false); - if (user_str != NULL && pg_strcasecmp(user_str, owner) == 0) { - pfree_ext(owner); - continue; /* skip current user's job */ - } - pfree_ext(owner); - update_pg_job(job_name, Anum_pg_job_enable, BoolGetDatum(false)); - } - list_free_deep(disable_job_names); - disable_job_names = NIL; -} - -/* - * @brief remove_scheduler_objects_from_owner - * Remove all external objects, including privileges from its owner. - * @param user_str - */ -void remove_scheduler_objects_from_owner(const char *user_str) -{ - Datum attribute_name = CStringGetTextDatum("owner"); - Datum attribute_value = CStringGetTextDatum(user_str); - Relation rel = heap_open(GsJobAttributeRelationId, AccessShareLock); - List *tuples = search_related_attribute(rel, attribute_name, attribute_value); - List *drop_object_names = NIL; - List *drop_object_types = NIL; - ListCell *lc = NULL; - foreach(lc, tuples) { - HeapTuple tuple = (HeapTuple)lfirst(lc); - bool isnull = false; - Datum object_name = heap_getattr(tuple, Anum_gs_job_attribute_job_name, rel->rd_att, &isnull); - Assert(!isnull); - Datum type = lookup_job_attribute(rel, object_name, CStringGetTextDatum("object_type"), &isnull, false); - drop_object_names = lappend(drop_object_names, DatumGetPointer(PG_DETOAST_DATUM_COPY(object_name))); - drop_object_types = lappend(drop_object_types, DatumGetPointer(type)); - } - heap_close(rel, AccessShareLock); - - /* call disable */ - lc = NULL; - ListCell *lc2 = NULL; - forboth(lc, drop_object_names, lc2, drop_object_types) { - Datum object_name = (Datum)lfirst(lc); - Datum object_type = (Datum)lfirst(lc2); - char *type_str = TextDatumGetCString(object_type); - check_object_type_matched(object_name, type_str); - delete_from_attribute(object_name); - delete_from_job_proc(object_name); - delete_from_argument(object_name); - delete_from_job(object_name); - - /* If it is a program, we need to disable all related jobs from other users */ - disable_related_job_with_program(object_name, type_str, user_str); - pfree(type_str); - } - const char *privilege_arr[] = {EXECUTE_ANY_PROGRAM_PRIVILEGE, CREATE_JOB_PRIVILEGE, - CREATE_EXTERNAL_JOB_PRIVILEGE, RUN_EXTERNAL_JOB_PRIVILEGE}; - int len = lengthof(privilege_arr); - for (int i = 0; i < len; i++) { - revoke_authorization(CStringGetTextDatum(user_str), CStringGetTextDatum(privilege_arr[i]), true); - } - list_free_deep(drop_object_names); - list_free_deep(drop_object_types); -} \ No newline at end of file -- 2.34.1 From 54c37c438d35bea3c3d38feecfc3e7a357458016 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:15:35 +0800 Subject: [PATCH 48/56] ADD file via upload --- .../process/job/gs_job_manager.cpp | 1746 +++++++++++++++++ 1 file changed, 1746 insertions(+) create mode 100644 src/gausskernel/process/job/gs_job_manager.cpp diff --git a/src/gausskernel/process/job/gs_job_manager.cpp b/src/gausskernel/process/job/gs_job_manager.cpp new file mode 100644 index 000000000..b444b4f35 --- /dev/null +++ b/src/gausskernel/process/job/gs_job_manager.cpp @@ -0,0 +1,1746 @@ +/* + * 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. + * ------------------------------------------------------------------------- + * + * gs_job_manager.cpp + * Functions to run/stop/execute/drop dbe jobs. + * + * IDENTIFICATION + * src/gausskernel/process/job/gs_job_manager.cpp + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "knl/knl_variable.h" +#include +#include "access/sysattr.h" +#include "access/xact.h" +#include "catalog/indexing.h" +#include "catalog/namespace.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_type.h" +#include "commands/alter.h" +#include "commands/comment.h" +#include "commands/dbcommands.h" +#include "commands/extension.h" +#include "commands/schemacmds.h" +#include "executor/spi.h" +#include "funcapi.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "pgxc/pgxc.h" +#include "tcop/utility.h" +#include "utils/builtins.h" +#include "utils/dbe_scheduler.h" +#include "utils/fmgroids.h" +#include "utils/formatting.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/rel_gs.h" +#include "utils/snapmgr.h" +#include "access/heapam.h" +#include "access/tableam.h" +#include "catalog/pg_job.h" +#include "catalog/pg_job_proc.h" +#include "catalog/pg_authid.h" +#include "catalog/pg_database.h" +#include "catalog/gs_job_argument.h" +#include "catalog/gs_job_attribute.h" +#include "fmgr.h" +#include "utils/syscache.h" +#include "pgxc/execRemote.h" + +/* Run job methods */ +static bool run_sql_job(Datum job_name, StringInfoData *buf); +static bool run_procedure_job(Datum job_name, StringInfoData *buf); +static char *run_external_job(Datum job_name); +/* + * @brief delete_by_syscache + * 通过搜索系统缓存执行简单的堆删除。 + * @param rel 目标关系 + * @param object_name 删除的键 + * @param cache_id 缓存ID + */ +static void delete_by_syscache(Relation rel, const Datum object_name, SysCacheIdentifier cache_id) +{ + /* 在系统缓存中查找对象 */ + CatCList *tuples = SearchSysCacheList1(cache_id, object_name); + if (tuples == NULL) { + return; + } + /* 循环遍历对象并删除 */ + for (int i = 0; i < tuples->n_members; i++) { + HeapTuple tuple = t_thrd.lsc_cxt.FetchTupleFromCatCList(tuples, i); + simple_heap_delete(rel, &tuple->t_self); + } + ReleaseSysCacheList(tuples); +} + +/* + * @brief delete_from_attribute + * 从gs_job_attribute表中删除。 + * @param object_name + */ +void delete_from_attribute(const Datum object_name) +{ + Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); + delete_by_syscache(gs_job_attribute_rel, object_name, JOBATTRIBUTENAME); + heap_close(gs_job_attribute_rel, NoLock); +} + +/* + * @brief delete_from_argument + * 从gs_job_argument表中删除。 + * @param job_name + */ +void delete_from_argument(const Datum object_name) +{ + Relation rel = heap_open(GsJobArgumentRelationId, RowExclusiveLock); + delete_by_syscache(rel, object_name, JOBARGUMENTNAME); + heap_close(rel, NoLock); +} + +/* + * @brief delete_from_job + * 从pg_job表中删除。 + * @param job_name + */ +void delete_from_job(const Datum job_name) +{ + Relation rel = heap_open(PgJobRelationId, RowExclusiveLock); + HeapTuple tuple = search_from_pg_job(rel, job_name); + if (tuple != NULL) { + simple_heap_delete(rel, &tuple->t_self); + } + heap_close(rel, NoLock); +} + +/* + * @brief delete_from_job_proc + * 从pg_job_proc表中删除。 + * @param job_name + */ +void delete_from_job_proc(const Datum job_name) +{ + Relation rel = heap_open(PgJobProcRelationId, RowExclusiveLock); + HeapTuple tuple = search_from_pg_job_proc_no_exception(rel, job_name); + if (tuple != NULL) { + simple_heap_delete(rel, &tuple->t_self); + } + heap_close(rel, NoLock); +} +HeapTuple search_from_pg_job(Relation pg_job_rel, Datum job_name) +{ + ScanKeyInfo scan_key_info1; + scan_key_info1.attribute_value = job_name; // 设置扫描键的属性值为job_name + scan_key_info1.attribute_number = Anum_pg_job_job_name; // 设置扫描键的属性编号为Anum_pg_job_job_name + scan_key_info1.procedure = F_TEXTEQ; // 设置扫描键的比较函数为F_TEXTEQ + + ScanKeyInfo scan_key_info2; + scan_key_info2.attribute_value = PointerGetDatum(u_sess->proc_cxt.MyProcPort->database_name); // 设置扫描键的属性值为当前数据库名称 + scan_key_info2.attribute_number = Anum_pg_job_dbname; // 设置扫描键的属性编号为Anum_pg_job_dbname + scan_key_info2.procedure = F_NAMEEQ; // 设置扫描键的比较函数为F_NAMEEQ + + List *tuples = search_by_sysscan_2(pg_job_rel, &scan_key_info1, &scan_key_info2); // 在pg_job_rel上执行扫描操作,并返回符合条件的元组列表 + if (tuples == NIL) { + return NULL; + } + Assert(list_length(tuples) == 1); // 断言元组列表长度为1 + if (list_length(tuples) != 1) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("find %d tuples match job_name %s in system table pg_job.", list_length(tuples), + TextDatumGetCString(job_name)), + errdetail("N/A"), errcause("job name is not exist"), erraction("Please check job_name"))); + } + HeapTuple tuple = (HeapTuple)linitial(tuples); // 获取元组列表中的第一个元组 + list_free_ext(tuples); // 释放元组列表的内存 + return tuple; // 返回元组 +} + + +/* + * @brief update_pg_job + * Update pg_job. + * @param job_name + * @param attribute_number + * @param attribute_value + */ +void update_pg_job(Datum job_name, int attribute_number, Datum attribute_value, bool isnull) +{ + Datum values[Natts_pg_job]; + bool nulls[Natts_pg_job]; + bool replaces[Natts_pg_job]; + errno_t rc = memset_s(replaces, sizeof(replaces), 0, sizeof(replaces)); + securec_check_c(rc, "\0", "\0"); + replaces[attribute_number - 1] = true; + if (!isnull) { + values[attribute_number - 1] = attribute_value; + nulls[attribute_number - 1] = false; + } else { + nulls[attribute_number - 1] = true; + } + + Relation pg_job_rel = heap_open(PgJobRelationId, RowExclusiveLock); + HeapTuple oldtuple = search_from_pg_job(pg_job_rel, job_name); + if (!HeapTupleIsValid(oldtuple)) { + heap_close(pg_job_rel, NoLock); + return; + } + + HeapTuple newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(pg_job_rel), values, nulls, replaces); + simple_heap_update(pg_job_rel, &newtuple->t_self, newtuple); + CatalogUpdateIndexes(pg_job_rel, newtuple); + heap_close(pg_job_rel, NoLock); + heap_freetuple_ext(newtuple); + heap_freetuple_ext(oldtuple); +} + +/* + * @brief update_pg_job_multi_columns + * Update multple columns from pg_job. + * @param job_name + * @param attribute_numbers attributes needed to be updated + * @param attribute_values attribute values(corresponds to attribute numbers) + * @param n number of columns + */ +void update_pg_job_multi_columns(const Datum job_name, const int *attribute_numbers, const Datum *attribute_values, + const bool *isnull, int n) +{ + Datum values[Natts_pg_job]; + bool nulls[Natts_pg_job]; + bool replaces[Natts_pg_job]; + error_t rc = memset_s(replaces, sizeof(replaces), 0, sizeof(replaces)); + securec_check_c(rc, "\0", "\0"); + for (int i = 0; i < n; i++) { + replaces[attribute_numbers[i] - 1] = true; + if (!isnull[i]) { + values[attribute_numbers[i] - 1] = attribute_values[i]; + nulls[attribute_numbers[i] - 1] = false; + } else { + nulls[attribute_numbers[i] - 1] = true; + } + } + + + Relation pg_job_rel = heap_open(PgJobRelationId, RowExclusiveLock); + HeapTuple oldtuple = search_from_pg_job(pg_job_rel, job_name); + if (!HeapTupleIsValid(oldtuple)) { + heap_close(pg_job_rel, NoLock); + return; + } + + HeapTuple newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(pg_job_rel), values, nulls, replaces); + simple_heap_update(pg_job_rel, &newtuple->t_self, newtuple); + CatalogUpdateIndexes(pg_job_rel, newtuple); + heap_close(pg_job_rel, NoLock); + heap_freetuple_ext(newtuple); + heap_freetuple_ext(oldtuple); +} + +/* + * @brief search_related_attribute + * Search all attributes that is somewhat related to the object. + * @param gs_job_attribute_rel + * @param attribute_name + * @param attribute_value + * @return List* List of related attribute tuples. + */ +List *search_related_attribute(Relation gs_job_attribute_rel, Datum attribute_name, Datum attribute_value) +{ + /* Job itself does not need related attributes */ + if (attribute_name == (Datum)0) { + return NIL; + } + ScanKeyInfo scan_key_info1; + scan_key_info1.attribute_value = attribute_name; + scan_key_info1.attribute_number = Anum_gs_job_attribute_attribute_name; + scan_key_info1.procedure = F_TEXTEQ; + ScanKeyInfo scan_key_info2; + scan_key_info2.attribute_value = attribute_value; + scan_key_info2.attribute_number = Anum_gs_job_attribute_attribute_value; + scan_key_info2.procedure = F_TEXTEQ; + List *tuples = search_by_sysscan_2(gs_job_attribute_rel, &scan_key_info1, &scan_key_info2); + return tuples; +} + +/* + * @brief disable_related_jobs_force + *//* + * @brief disable_related_jobs_force + * + * @param gs_job_attribute_rel + * @param disable_job_names 要禁用的作业名称列表 + */ +static void disable_related_jobs_force(Relation gs_job_attribute_rel, List *disable_job_names) +{ + ListCell *lc = NULL; + foreach (lc, disable_job_names) { + Datum job_name = PointerGetDatum(lfirst(lc)); + // 禁用指定名称的作业 + update_pg_job(job_name, Anum_pg_job_enable, BoolGetDatum(false)); + } +} + +/* + * @brief reset_job_class + * + * @param gs_job_attribute_rel + * @param disable_job_names 在设置默认 job_class 前需要先禁用这些作业 + * @param attribute_name 要重置为默认 job_class 的属性名称 + * @param force 是否强制执行重置操作 + */ +static void reset_job_class(Relation gs_job_attribute_rel, List *disable_job_names, Datum attribute_name, bool force) +{ + if (!force && disable_job_names) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Job class is refered by at least one job."), + errdetail("N/A"), errcause("job class is used"), + erraction("Please pass force true or drop job first"))); + } + + ListCell *lc = NULL; + foreach (lc, disable_job_names) { + Datum job_name = PointerGetDatum(lfirst(lc)); + // 将指定作业的 attribute_name 属性重置为默认 job_class + update_attribute(job_name, attribute_name, CStringGetTextDatum("DEFAULT_JOB_CLASS")); + } +} + + +/* + * @brief search_related_jobs + * search all related jobs. + * @param object_name + * @param attribute_name + * @param force + * @return List* + */ + //search_related_jobs 函数:用于查找所有与给定对象和属性名相关的任务,并返回任务名称列表。 +static List *search_related_jobs(Relation gs_job_attribute_rel, Datum object_name, Datum attribute_name, bool force) +{ + List *tuples = search_related_attribute(gs_job_attribute_rel, attribute_name, object_name); + // If force is set to FALSE, a class being dropped must not be referenced by any jobs, otherwise an error occurs. + if (!force && list_length(tuples) != 0) { + HeapTuple tuple = (HeapTuple)linitial(tuples); + bool isNull = false; + Datum related_name = + heap_getattr(tuple, Anum_gs_job_attribute_job_name, RelationGetDescr(gs_job_attribute_rel), &isNull); + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_IN_USE), + errmsg("%s %s refered by job %s", TextDatumGetCString(attribute_name), + TextDatumGetCString(object_name), TextDatumGetCString(related_name)), + errdetail("N/A"), errcause("attribute is used"), + erraction("Please drop object %s", DatumGetPointer(object_name)))); + } + List *disable_job_names = NIL; + ListCell *lc = NULL; + foreach (lc, tuples) { + HeapTuple tuple = (HeapTuple)lfirst(lc); + bool isNull = false; + Datum disable_job_name = + heap_getattr(tuple, Anum_gs_job_attribute_job_name, RelationGetDescr(gs_job_attribute_rel), &isNull); + Assert(!isNull); + disable_job_names = lappend(disable_job_names, DatumGetPointer(PG_DETOAST_DATUM_COPY(disable_job_name))); + } + list_free_deep(tuples); + return disable_job_names; +} + +/* + * @brief drop_inline_program + * Drop inline program if exists. + * @param job_name + */ + //drop_inline_program 函数:删除指定任务的内联程序(如果存在)及其相关信息。 +void drop_inline_program(const Datum job_name) +{ + Datum attribute_name = CStringGetTextDatum("program_name"); + + Relation rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); + bool isnull = false; + Datum program_name = lookup_job_attribute(rel, job_name, attribute_name, &isnull, true); + heap_close(rel, NoLock); + if (!PointerIsValid(program_name)) { + return; + } + + char *program_name_str = TextDatumGetCString(program_name); + if (strncmp(INLINE_JOB_PROGRAM_PREFIX, program_name_str, strlen(INLINE_JOB_PROGRAM_PREFIX)) == 0) { + delete_from_attribute(program_name); + delete_from_job_proc(program_name); + delete_from_argument(program_name); + } +} + +/* + * @brief drop_single_object_name + * Drop one object, disable all related objects. + * @param object_name + * @param object_type + * @param force + * @param simple when set to true, do not disable relatied objects + */ + //drop_single_object_name 函数:删除指定的对象,并禁用所有相关对象。 +static void drop_single_object_name(Datum object_name, const char *object_type, bool force) +{ + check_object_type_matched(object_name, object_type); + delete_from_attribute(object_name); + delete_from_job_proc(object_name); + delete_from_argument(object_name); + + Datum attribute_name; + if (pg_strcasecmp(object_type, "job_class") == 0) { + attribute_name = CStringGetTextDatum("job_class"); + } else if (pg_strcasecmp(object_type, "program") == 0) { + attribute_name = CStringGetTextDatum("program_name"); + } else if (pg_strcasecmp(object_type, "schedule") == 0) { + attribute_name = CStringGetTextDatum("schedule_name"); + } else { + Assert(false); + return; + } + + Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); + List *disable_job_names = search_related_jobs(gs_job_attribute_rel, object_name, attribute_name, force); + disable_related_jobs_force(gs_job_attribute_rel, disable_job_names); + if (pg_strcasecmp(object_type, "job_class") == 0 && list_length(disable_job_names) > 0) { + reset_job_class(gs_job_attribute_rel, disable_job_names, attribute_name, force); + } + heap_close(gs_job_attribute_rel, NoLock); + list_free_deep(disable_job_names); + disable_job_names = NIL; + pfree(DatumGetPointer(attribute_name)); +} + +/* + * @brief drop_single_job_class_internal + * Drop a single job class. + * Note: + * Dropping a job class requires the MANAGE SCHEDULER system privilege. + */ + //此函数用于删除单个作业类。 +void drop_single_job_class_internal(PG_FUNCTION_ARGS) +{ + check_object_is_visible(PG_GETARG_DATUM(0), false); + Datum job_class_name = PG_GETARG_DATUM(0); + char *job_class_str = TextDatumGetCString(job_class_name); + if (pg_strcasecmp(job_class_str, "DEFAULT_JOB_CLASS") == 0) { + return; + } + bool force = PG_GETARG_BOOL(1); + drop_single_object_name(job_class_name, "job_class", force); +} + +/* + * @brief drop_single_program_internal + * Drop a single program. + */ + //此函数用于删除单个程序。 +void drop_single_program_internal(PG_FUNCTION_ARGS) +{ + check_object_is_visible(PG_GETARG_DATUM(0), false); + Datum program_name = PG_GETARG_DATUM(0); + + char *program_type_str = get_attribute_value_str(program_name, "program_type", AccessShareLock, true); + if (program_type_str == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Cannot find program type of program %s.", TextDatumGetCString(program_name)), + errdetail("Invalid program format."), errcause("N/A"), + erraction("Please check the program name."))); + } + + if (pg_strcasecmp(program_type_str, EXTERNAL_JOB_TYPE) == 0) { + check_privilege(get_role_name_str(), CREATE_EXTERNAL_JOB_PRIVILEGE); + } + pfree_ext(program_type_str); + + bool force = PG_GETARG_BOOL(1); + drop_single_object_name(program_name, "program", force); +} + +/* + * @brief drop_single_schedule_internal + * Drop a single schedule. + */ + //此函数用于删除单个调度。 +void drop_single_schedule_internal(PG_FUNCTION_ARGS) +{ + check_object_is_visible(PG_GETARG_DATUM(0), false); + Datum schedule_name = PG_GETARG_DATUM(0); + bool force = PG_GETARG_BOOL(1); + drop_single_object_name(schedule_name, "schedule", force); +} + +/* + * @brief drop_credential_internal + * Drop a single credential. + */ + //此函数用于删除单个凭据。 +void drop_credential_internal(PG_FUNCTION_ARGS) +{ + if (!superuser()) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Fail to drop credential."), + errdetail("Insufficient privilege to drop credential."), errcause("N/A"), + erraction("Please login in with initial user or contact database administrator."))); + } + const Datum credential_name = PG_GETARG_DATUM(0); + check_object_type_matched(credential_name, "credential"); + bool force = PG_GETARG_BOOL(1); + if (!force) { + Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); + Datum credential_attribute_name = CStringGetTextDatum("credential_name"); + List *tuples = search_related_attribute(gs_job_attribute_rel, credential_attribute_name, credential_name); + pfree(DatumGetPointer(credential_attribute_name)); + credential_attribute_name = Datum(0); + if (list_length(tuples) > 0) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OBJECT_IN_USE), + errmsg("Fail to drop credential."), + errdetail("Credential %s is refered by jobs.", TextDatumGetCString(credential_name)), + errcause("Credential is in use."), + erraction("Please set force flag to true or drop all associated jobs."))); + } + heap_close(gs_job_attribute_rel, NoLock); + } + delete_from_attribute(credential_name); +} + +/* + * @brief revoke_authorization + * revoke authorization. + * @param username + * @param privilege + * @param skip_disable for drop role only, we do not need to disable anything since all + * objects are dropped by then + */ +static void revoke_authorization(Datum username, Datum privilege, bool skip_disable = false) +{ + Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); + HeapTuple old_tuple = SearchSysCache2(JOBATTRIBUTENAME, username, privilege); + if (old_tuple == NULL) { + heap_close(gs_job_attribute_rel, NoLock); + return; + } + simple_heap_delete(gs_job_attribute_rel, &old_tuple->t_self); + ReleaseSysCache(old_tuple); + heap_close(gs_job_attribute_rel, NoLock); + + if (skip_disable) { + return; + } + + char *privilege_str = TextDatumGetCString(privilege); + char *username_str = TextDatumGetCString(username); + if (pg_strcasecmp(privilege_str, EXECUTE_ANY_PROGRAM_PRIVILEGE) == 0) { + disable_shared_job_from_owner(username_str); + } else if (pg_strcasecmp(privilege_str, RUN_EXTERNAL_JOB_PRIVILEGE) == 0) { + disable_external_job_from_owner(username_str); + } + pfree_ext(username_str); + pfree_ext(privilege_str); +} + +/* + * @brief revoke_user_authorization_internal + * Revoke a user's authorization. + */ +void revoke_user_authorization_internal(PG_FUNCTION_ARGS) +{ + const Datum username = get_role_datum(PG_GETARG_DATUM(0)); + const Datum privilege = PG_GETARG_DATUM(1); + if (!superuser()) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Fail to revoke authorization."), + errdetail("Insufficient privilege to revoke authorization."), errcause("N/A"), + erraction("Please login in with initial user or contact database administrator."))); + } + check_authorization_valid(privilege); + revoke_authorization(username, privilege); +} + +/* + * @brief lookup_job_attribute + * Look up one attribute. + * @param rel gs_job_attribute relation + * @param object_name Object name(can be any job related objects) + * @param attribute The attribute we looking for + * @param isnull null pointer, mark whether the look up result is null + * @return Datum return the attribute value + */ +Datum lookup_job_attribute(Relation rel, Datum object_name, Datum attribute, bool *isnull, bool miss_ok) +{ + HeapTuple attribute_tuple = SearchSysCache2(JOBATTRIBUTENAME, object_name, attribute); + if (!HeapTupleIsValid(attribute_tuple)) { + if (miss_ok) { + return Datum(0); + } + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Can not find attribute \'%s\' of object name \'%s\'.", TextDatumGetCString(attribute), + TextDatumGetCString(object_name)), + errdetail("N/A"), errcause("attribute is not exist"), erraction("Please check object name"))); + } + Datum attr = heap_getattr(attribute_tuple, Anum_gs_job_attribute_attribute_value, rel->rd_att, isnull); + Assert((*isnull && !PointerIsValid(attr)) || (!(*isnull) && PointerIsValid(attr))); + Datum attr_cpy = Datum(0); + if (PointerIsValid(attr)) { + attr_cpy = PointerGetDatum(PG_DETOAST_DATUM_COPY(attr)); + } + ReleaseSysCache(attribute_tuple); + + return attr_cpy; +} + +/* + * @brief batch_lookup_job_attribute + * Look up attribute in a batch with given attribute look up names. + * @param attributes Job attributes with attribute name pre-filled + * @param n Number of attributes wanted + */ +void batch_lookup_job_attribute(JobAttribute *attributes, int n) +{ + if (attributes == NULL) { + return; + } + + Relation rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); + for (int i = 0; i < n; i++) { + attributes[i].value = lookup_job_attribute(rel, attributes[i].object_name, attributes[i].name, + &attributes[i].null, false); + } + heap_close(rel, NoLock); +} + +/* + * @brief lookup_program_argument + * Look up one program argument. + * @param rel gs_job_argument relation + * @param job_name Job name + * @param program_name Program name + * @param position Position of the argument + * @param argument Job argument context + */ +void lookup_program_argument(Relation rel, Datum job_name, Datum program_name, int position, JobArgument *argument) +{ + HeapTuple argument_tuple = SearchSysCache2(JOBARGUMENTPOSITION, job_name, Int32GetDatum(position)); + if (!HeapTupleIsValid(argument_tuple)) { + argument_tuple = SearchSysCache2(JOBARGUMENTPOSITION, program_name, Int32GetDatum(position)); + if (!HeapTupleIsValid(argument_tuple)) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Can not find argument info of object \'%s\'.", TextDatumGetCString(job_name)), + errdetail("N/A"), errcause("argument information not found"), + erraction("Please check object name"))); + } + } + bool isnull = true; + /* Name: type */ + Datum type = heap_getattr(argument_tuple, Anum_gs_job_argument_argument_type, rel->rd_att, &isnull); + if (!isnull) { + argument->argument_type = pstrdup((char *)type); + } else { + argument->argument_type = NULL; + } + + /* Datum: argument_value */ + Datum argument_value = heap_getattr(argument_tuple, Anum_gs_job_argument_argument_value, rel->rd_att, &isnull); + if (!isnull) { + argument->argument_value = TextDatumGetCString(argument_value); + } else { + argument->argument_value = NULL; + } + + if (argument->argument_value != NULL) { + ReleaseSysCache(argument_tuple); + return; + } + + /* Datum: default_value */ + Datum default_value = heap_getattr(argument_tuple, Anum_gs_job_argument_default_value, rel->rd_att, &isnull); + if (!isnull) { + argument->argument_value = TextDatumGetCString(default_value); + } else { + argument->argument_value = NULL; + } + + if (argument->argument_value == NULL) { + ReleaseSysCache(argument_tuple); + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Cannot get argument value of job %s.", TextDatumGetCString(job_name)), + errdetail("N/A"), errcause("argument information not found"), + erraction("Please check object name"))); + } + ReleaseSysCache(argument_tuple); +} + +/* + * @brief batch_lookup_program_argument + * Allow us to look up multiple arguments(all the available arguments) in a batch. + * @param job_name Job name + * @param program_name Program name + * @param number_of_arguments Number of arguments + */ +JobArgument *batch_lookup_program_argument(Datum job_name, Datum program_name, int number_of_arguments) +{ + if (number_of_arguments <= 0) { + return NULL; + } + JobArgument *arguments = (JobArgument *)palloc0(number_of_arguments * sizeof(JobArgument)); + Relation rel = heap_open(GsJobArgumentRelationId, AccessShareLock); + for (int i = 0; i < number_of_arguments; i++) { + lookup_program_argument(rel, job_name, program_name, i + 1, arguments + i); + if (arguments[i].argument_value == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Can not find argument %d in system table gs_job_argument.", i + 1), + errdetail("N/A"), errcause("argument %d is not defined", i + 1), + erraction("Please check program arguments"))); + } + } + heap_close(rel, NoLock); + return arguments; +} + +/* + * @brief lookup_credential_username + * Look up credential username. + * @param credential_name + * @return char* + */ +char *lookup_credential_username(Datum job_name) +{ + bool use_default = false; + char *job_type_str = get_job_type(job_name, false); + bool is_shell_job = pg_strcasecmp(job_type_str, EXTERNAL_JOB_TYPE) == 0; + pfree_ext(job_type_str); + if (!is_shell_job) { + return NULL; + } + Datum credential_name = get_attribute_value(job_name, "credential_name", AccessShareLock, true, true); + if (credential_name == Datum(0)) { + use_default = true; + credential_name = CStringGetTextDatum(DEFAULT_CREDENTIAL_NAME); + } + char *attribute_value_str = get_attribute_value_str(credential_name, "object_type", AccessShareLock, true, true); + if (attribute_value_str == NULL || pg_strcasecmp(attribute_value_str, "credential") != 0) { + if (use_default) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("No database wise credential found."), + errdetail("Need to create default credential for database with name 'db_credential'"), + errcause("No credential found."), + erraction("Please create a default or custom credential."))); + return NULL; /* compiler happy */ + } + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Fail to look up credential."), + errdetail("Credential specified does not exist."), + errcause("No credential found."), + erraction("Please check credential name."))); + } + pfree_ext(attribute_value_str); + char *username = get_attribute_value_str(credential_name, "username", AccessShareLock, false, false); + return username; +} + +/* + * @brief lookup_job_value + * Look up job values. + * @param job_name + * @return JobValue* + */ +JobValue *lookup_job_value(Datum job_name) +{ + Relation pg_job_rel = heap_open(PgJobRelationId, AccessShareLock); + HeapTuple tuple = search_from_pg_job(pg_job_rel, job_name); + if (!HeapTupleIsValid(tuple)) { + heap_close(pg_job_rel, AccessShareLock); + return NULL; + } + JobValue *job_value = (JobValue *)palloc0(sizeof(JobValue)); + bool isnull = false; + + job_value->end_date = DatumGetTimestamp(heap_getattr(tuple, Anum_pg_job_end_date, pg_job_rel->rd_att, &isnull)); + if (isnull) { + job_value->end_date = get_scheduler_max_timestamp(); + } + job_value->nspname = DatumGetPointer(heap_getattr(tuple, Anum_pg_job_nspname, pg_job_rel->rd_att, &isnull)); + if (isnull) { + job_value->nspname = NULL; + } else { + job_value->nspname = pstrdup(job_value->nspname); + } + job_value->interval = TextDatumGetCString(heap_getattr(tuple, Anum_pg_job_interval, pg_job_rel->rd_att, &isnull)); + if (isnull || pg_strcasecmp(job_value->interval, "null") == 0) { + pfree(job_value->interval); + job_value->interval = NULL; + } + job_value->fail_count = DatumGetInt16(heap_getattr(tuple, Anum_pg_job_failure_count, pg_job_rel->rd_att, &isnull)); + if (isnull) { + job_value->fail_count = 0; + } + heap_close(pg_job_rel, AccessShareLock); + return job_value; +} + +/* + * @brief make_job_proc_value + * Context struct initialization. + * @param job_action Job action datum + * @return JobProcValue* Out context + */ +JobProcValue *make_job_proc_value(Datum job_action) +{ + JobProcValue *job_proc_value = (JobProcValue *)palloc0(sizeof(JobProcValue)); + job_proc_value->action = job_action; + job_proc_value->action_str = TextDatumGetCString(job_action); + return job_proc_value; +} + +/* + * @brief make_job_attribute_value + * Context struct initialization. + * @param attributes Attribute keys + * @return JobAttributeValue* Out context + */ +JobAttributeValue *make_job_attribute_value(JobAttribute *attributes) +{ + enum {PROGRAM_TYPE = 0, PROGRAM_ENABLED, NUM_OF_ARGS, AUTO_DROP, JOB_CLASS}; + JobAttributeValue *job_attribute_value = (JobAttributeValue *)palloc0(sizeof(JobAttributeValue)); + job_attribute_value->job_type = attributes[PROGRAM_TYPE].value; + attributes[PROGRAM_TYPE].value = Datum(0); + job_attribute_value->program_enable = TextToBool(attributes[PROGRAM_ENABLED].value); + job_attribute_value->auto_drop = TextToBool(attributes[AUTO_DROP].value); + job_attribute_value->number_of_arguments = TextToInt32(attributes[NUM_OF_ARGS].value); + job_attribute_value->job_class = attributes[JOB_CLASS].value; + + return job_attribute_value; +} + +/* + * @brief make_job_target + * Make the job target by put attributes into a well organized structure. + * @param job_name Job name + * @param program_name Associated program name(can be a inlined program) + * @param attributes The job attributes + * @return JobTarget* The structure created + */ +static JobTarget *make_job_target(Datum job_name, Datum program_name, JobAttribute *attributes) +{ + Datum job_id; + Datum job_action; + + /* Lookup id, action attributes */ + lookup_pg_job_proc(job_name, &job_id, &job_action); + if (job_action == Datum(0)) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("job %s's action is undefined.", TextDatumGetCString(job_name)), + errdetail("N/A"), errcause("job's action is undefined"), erraction("Please check job_name"))); + } + + /* Fill up job targets */ + JobTarget *job_target = (JobTarget *)palloc0(sizeof(JobTarget)); + job_target->job_name = job_name; + job_target->id = job_id; + job_target->job_proc_value = make_job_proc_value(job_action); + job_target->job_attribute_value = make_job_attribute_value(attributes); + job_target->job_attribute_value->username = lookup_credential_username(job_name); + job_target->job_attribute_value->program_name = program_name; + job_target->arguments = batch_lookup_program_argument(job_name, program_name, + job_target->job_attribute_value->number_of_arguments); + job_target->job_value = lookup_job_value(job_name); + return job_target; +} + +/* + * @brief get_job_target + * Get the Job Target object. + * @param job_name Job name datum + * @return JobTarget* Out context + */ +static JobTarget *get_job_target(Datum job_name) +{ + Datum program_name = get_attribute_value(job_name, "program_name", AccessShareLock); + + /* Lookup corresponding attributes */ + + const char *attribute_names[] = {"program_type", "enabled", "number_of_arguments", "auto_drop", "job_class"}; + const Datum object_names[] = {program_name, program_name, program_name, job_name, job_name}; + int count = lengthof(attribute_names); + JobAttribute *lookup_attributes = (JobAttribute *)palloc0(sizeof(JobAttribute) * count); + for (int i = 0; i < count; i++) { + lookup_attributes[i].object_name = object_names[i]; + lookup_attributes[i].name = CStringGetTextDatum(attribute_names[i]); + } + batch_lookup_job_attribute(lookup_attributes, count); + + /* Job prep */ + JobTarget *job_target = make_job_target(job_name, program_name, lookup_attributes); + for (int i = 0; i < count; i++) { + lookup_attributes[i].object_name = object_names[i]; + pfree(DatumGetPointer(lookup_attributes[i].name)); + if (lookup_attributes[i].value != Datum(0)) { + pfree(DatumGetPointer(lookup_attributes[i].value)); + } + } + pfree_ext(lookup_attributes); + return job_target; +} + +/* + * @brief check_program_type_argument + * Perform a simple check on program type argument. + * @param program_type + * @param number_of_arguments + */ +void check_program_type_argument(Datum program_type, int number_of_arguments) +{ + if (pg_strcasecmp(TextDatumGetCString(program_type), PLSQL_JOB_TYPE) == 0 && number_of_arguments != 0) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Invalid program type or argument"), + errdetail("Program type PLSQL_BLOCK must has no argument"), + errcause("Program type is PLSQL_BLOCK, so the number of arguments must be 0"), + erraction("Please check program type or argument param"))); + } + const int max_arguments = 255; + if (number_of_arguments > max_arguments || number_of_arguments < 0) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Invalid program arguments"), + errdetail("Program arguments must less euqal than 255 and greater euqal than zero"), + errcause("Program arguments must less euqal than 255"), + erraction("Please check program argument param"))); + } +} + +/* + * @brief check_str_valid + * Check if a user input string contains danger characters. + * @param str + */ +static char *replace_all_danger_character(const char *str) +{ + if (strstr(str, "\\\"") != NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_NAME), + errmsg("Fail to parse script parameter."), + errdetail("Parameter contains escaped double quotes '\\\"'."), errcause("Command is invalid"), + erraction("Please enter a valid command"))); + } + StringInfoData buffer; + initStringInfo(&buffer); + appendStringInfoString(&buffer, "\\\""); /* \" for start of param */ + int len = strlen(str); + for (int i = 0; i < len; i++) { + if (str[i] == '"') { + appendStringInfoString(&buffer, "\\\\\\\""); /* \\\" for escaping the escaped double quote */ + } else if (str[i] == '\\' || str[i] == '|') { + appendStringInfoString(&buffer, "\\"); /* \ for escaping other dangerous str[i] character */ + appendStringInfoChar(&buffer, str[i]); + } else { + appendStringInfoChar(&buffer, str[i]); + } + } + appendStringInfoString(&buffer, "\\\""); /* \" */ + return buffer.data; +} + +/* + * @brief check_real_path_valid + * Check if a user input string is a valid real path. + * @param file_name + */ +static void check_real_path_valid(const Datum file_name) +{ + char *command = TextDatumGetCString(file_name); + char path_name_arr[PATH_MAX + 1] = {0}; + char *path_name = realpath(command, path_name_arr); + if (path_name == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_NAME), + errmsg("Fail to execute external job."), + errdetail("Cannot execute external script with given path."), errcause("Command is invalid"), + erraction("Please enter a valid command"))); + } + const char *danger_character_list[] = {"|", ";", "&", "$", "<", ">", "`", "\\", "'", "\"", "{", + "}", "(", ")", "[", "]", "~", "*", "?", "!", "\n", NULL}; + for (int i = 0; danger_character_list[i] != NULL; i++) { + if (strstr(path_name, danger_character_list[i]) != NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_NAME), + errmsg("invalid token \"%s\"", danger_character_list[i]), + errdetail("str contains invalid character"), errcause("str is invalid"), + erraction("Please enter a valid str"))); + } + } + pfree(command); +} + +/* + * @brief run_backend_job_internal + * Runs a job immediately by reset job_status and next_run_time. + * Note: use_current_session is not functioning. + */ +void run_backend_job_internal(PG_FUNCTION_ARGS) +{ + check_object_is_visible(PG_GETARG_DATUM(0)); + Datum job_name = PG_GETARG_DATUM(0); + check_object_type_matched(job_name, "job"); + if (is_job_running(job_name)) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_STATUS), + errmsg("Job is still running."), + errdetail("Cannot run job when job is already running."), + errcause("Job is still running."), + erraction("Please try again later."))); + } + JobTarget *job_target = get_job_target(job_name); /* for check only */ + pfree_ext(job_target); + + /* Run job */ + Datum current_time = DirectFunctionCall1(timestamptz_timestamp, TimestampTzGetDatum(GetCurrentTimestamp())); + Datum job_related_attr[] = {current_time, CharGetDatum(PGJOB_SUCC_STATUS)}; + int job_related_num[] = {Anum_pg_job_next_run_date, Anum_pg_job_job_status}; + bool isnull[] = {false, false}; + update_pg_job_multi_columns(job_name, (int *)job_related_num, job_related_attr, isnull, lengthof(job_related_attr)); +} + +/* + * @brief check_sql_job_ready + * Check if job is ready to run. + * @param job_target + */ +static void check_sql_job_ready(JobTarget *job_target) +{ + if (!job_target->job_attribute_value->program_enable) { + StartTransactionCommand(); + update_pg_job(job_target->job_name, Anum_pg_job_enable, BoolGetDatum(false)); + CommitTransactionCommand(); + char *program_name_str = TextDatumGetCString(job_target->job_attribute_value->program_name); + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_STATUS), + errmsg("program %s of job %s is disabled.", + program_name_str, TextDatumGetCString(job_target->job_name)), + errdetail("N/A"), errcause("program of job is disabled"), + erraction("Please enable program %s of job first", program_name_str))); + } + check_program_type_argument(job_target->job_attribute_value->job_type, + job_target->job_attribute_value->number_of_arguments); + check_object_is_visible(job_target->job_attribute_value->program_name); +} + +/* + * @brief check_external_job_ready + * + * @param job_target + */ +static void check_external_job_ready(JobTarget *job_target) +{ + check_sql_job_ready(job_target); + check_real_path_valid(PointerGetDatum(job_target->job_proc_value->action)); + + for (int i = 0; i < job_target->job_attribute_value->number_of_arguments; i++) { + job_target->arguments[i].argument_value = replace_all_danger_character(job_target->arguments[i].argument_value); + } + if (job_target->job_attribute_value->username == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("job %s's credential username is undefined.", + TextDatumGetCString(job_target->job_name)), + errdetail("N/A"), errcause("credential username is undefined"), + erraction("Please check job_name"))); + } + check_privilege(get_role_name_str(), RUN_EXTERNAL_JOB_PRIVILEGE); +} + +/* + * @brief expire_backend_job + * Disable or drop job when job expired. + */ +void expire_backend_job(Datum job_name, bool auto_drop) +{ + start_xact_command(); + if (auto_drop) { + drop_inline_program(job_name); + delete_from_attribute(job_name); + delete_from_job_proc(job_name); + delete_from_argument(job_name); + delete_from_job(job_name); + } else { + update_pg_job(job_name, Anum_pg_job_enable, BoolGetDatum(false)); + } + finish_xact_command(); +} + +/* + * @brief refresh_backend_job_status + */ +static bool refresh_backend_job_status(JobTarget *job_target, bool after_run) +{ + /* after run */ + if (after_run && job_target->job_value->interval == NULL) { + expire_backend_job(job_target->job_name, job_target->job_attribute_value->auto_drop); + return true; + } + + /* before run */ + TimestampTz cur_timestamp = GetCurrentTimestamp(); + bool expire = DatumGetBool(DirectFunctionCall2(timestamptz_gt_timestamp, cur_timestamp, + job_target->job_value->end_date)); + if (expire) { + expire_backend_job(job_target->job_name, job_target->job_attribute_value->auto_drop); + return true; + } + return false; +} + +/* + * @brief run_job_internal + * Runs a job immediately, only when external job, others ereport error + * Note: use_current_session is not functioning. + */ +Datum run_foreground_job_internal(PG_FUNCTION_ARGS) +{ + check_object_is_visible(PG_GETARG_DATUM(0)); + Datum job_name = PG_GETARG_DATUM(0); + check_object_type_matched(job_name, "job"); + char *program_type = get_job_type(job_name, false); + if (pg_strcasecmp(program_type, EXTERNAL_JOB_TYPE) != 0) { // shell + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("when run current session job, program_type %s is not supported.", program_type), + errdetail("program_type not supported"), errcause("N/A"), + erraction("please check program type"))); + } + pfree(program_type); + char *res = run_external_job(job_name); + Assert(res != NULL); + Datum res_text = CStringGetTextDatum(res); + pfree_ext(res); + return res_text; +} + +/* + * @brief run_sql_job + * Run sql job. + * @param job_name + * @param buf + * @return true + * @return false + */ +static bool run_sql_job(Datum job_name, StringInfoData *buf) +{ + JobTarget *job_target = get_job_target(job_name); + check_sql_job_ready(job_target); + if (t_thrd.role == JOB_WORKER) { + if (refresh_backend_job_status(job_target, false)) { + return true; + } + } + if (buf->data[0] == '\0' && job_target->job_value->nspname != NULL) { + appendStringInfo(buf, "set current_schema=%s;", quote_identifier(job_target->job_value->nspname)); + } + appendStringInfo(buf, "%s", job_target->job_proc_value->action_str); + execute_simple_query(buf->data); + if (t_thrd.role == JOB_WORKER) { + (void)refresh_backend_job_status(job_target, true); + } + return true; +} + +/* + * @brief run_procedure_job + * Run stored procedure job. + * @param job_name + * @param buf + * @return true + * @return false + */ +static bool run_procedure_job(Datum job_name, StringInfoData *buf) +{ + JobTarget *job_target = get_job_target(job_name); + check_sql_job_ready(job_target); + if (t_thrd.role == JOB_WORKER) { + if (refresh_backend_job_status(job_target, false)) { + return true; + } + } + if (buf->data[0] == '\0' && job_target->job_value->nspname != NULL) { + appendStringInfo(buf, "set current_schema=%s;", quote_identifier(job_target->job_value->nspname)); + } + appendStringInfoString(buf, "call "); + appendStringInfoString(buf, job_target->job_proc_value->action_str); + appendStringInfoString(buf, "("); + for (int i = 0; i < job_target->job_attribute_value->number_of_arguments; i++) { + if (i > 0) { + appendStringInfoString(buf, ", "); + } + appendStringInfoString(buf, "'"); + appendStringInfoString(buf, job_target->arguments[i].argument_value); + appendStringInfoString(buf, "'::"); + appendStringInfoString(buf, job_target->arguments[i].argument_type); + } + appendStringInfoString(buf, ");"); + execute_simple_query(buf->data); + if (t_thrd.role == JOB_WORKER) { + (void)refresh_backend_job_status(job_target, true); + } + return true; +} + +/* + * @brief ssh_run_external_program + * Create a SSH connection string for execute a program + * @param username + * @param action Program action + * @return char* + */ +static char *ssh_run_external_program(const char *username, const char *action) +{ + StringInfoData ssh_buf; + initStringInfo(&ssh_buf); + appendStringInfoString(&ssh_buf, "ssh "); + appendStringInfoString(&ssh_buf, username); + appendStringInfoString(&ssh_buf, "@localhost \""); + appendStringInfoString(&ssh_buf, action); + appendStringInfoString(&ssh_buf, "\""); + appendStringInfoString(&ssh_buf, " 2>&1"); + + FILE *ssh_stream = popen(ssh_buf.data, "r"); + if (ssh_stream == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_OPERATE_FAILED), + errmsg("ssh to username failed"), errdetail("cannot ssh to username, popen fail"), + errcause("N/A"), + erraction("Please append ~/.ssh/id_rsa.pub to username's homepath/.ssh/authorized_keys"))); + } + StringInfoData res_buf; + initStringInfo(&res_buf); + const int bufferLen = 1024; + char buffer[bufferLen]; + while (fgets(buffer, sizeof(buffer), ssh_stream) != NULL) { + buffer[bufferLen - 1] = '\0'; + appendStringInfoString(&res_buf, buffer); + } + pclose(ssh_stream); + pfree(ssh_buf.data); + return res_buf.data; +} + +/* + * @brief run_external_job + * @param job_name + */ +static char *run_external_job(Datum job_name) +{ + JobTarget *job_target = get_job_target(job_name); + Assert(job_target->job_proc_value->action != Datum(0)); + check_external_job_ready(job_target); + if (t_thrd.role == JOB_WORKER) { + if (refresh_backend_job_status(job_target, false)) { + return NULL; + } + } + StringInfoData action_buf; + initStringInfo(&action_buf); + appendStringInfoString(&action_buf, job_target->job_proc_value->action_str); + for (int i = 0; i < job_target->job_attribute_value->number_of_arguments; i++) { + appendStringInfoString(&action_buf, " "); + appendStringInfoString(&action_buf, job_target->arguments[i].argument_value); + } + char *res = ssh_run_external_program(job_target->job_attribute_value->username, action_buf.data); + if (t_thrd.role == JOB_WORKER) { + (void)refresh_backend_job_status(job_target, true); + } + return res; +} + +/* + * @brief execute_backend_scheduler_job + * Execute job main. + */ +bool execute_backend_scheduler_job(Datum job_name, StringInfoData *buf) +{ + if (job_name == Datum(0)) { + return false; + } + char *program_type = get_job_type(job_name, true); + if (!PointerIsValid(program_type)) { + return false; + } + if (pg_strcasecmp(program_type, PLSQL_JOB_TYPE) == 0) { + return run_sql_job(job_name, buf); + } + if (pg_strcasecmp(program_type, PRECEDURE_JOB_TYPE) == 0) { + return run_procedure_job(job_name, buf); + } + if (pg_strcasecmp(program_type, EXTERNAL_JOB_TYPE) != 0) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("program_type %s cannot be recognized.", program_type), + errdetail("program_type crashed"), errcause("N/A"), + erraction("please check program type"))); + } + pfree(program_type); + (void)run_external_job(job_name); + return true; +} + +/* + * @brief stop_single_job_force + * Send a SIGINT/SIGTERM to job worker. + * @param job_name + * @param force + */ +static void stop_single_job_force(Datum job_name, bool force) +{ + Relation pg_job_rel = heap_open(PgJobRelationId, RowExclusiveLock); + HeapTuple tuple = search_from_pg_job(pg_job_rel, job_name); + if (!HeapTupleIsValid(tuple)) { + heap_close(pg_job_rel, NoLock); + return; + } + Form_pg_job pg_job_value = (Form_pg_job)GETSTRUCT(tuple); + if (pg_job_value->job_status != PGJOB_RUN_STATUS || pg_job_value->current_postgres_pid == -1) { + heap_close(pg_job_rel, NoLock); + heap_freetuple_ext(tuple); + return; + } + if (!force) { + gs_signal_send(pg_job_value->current_postgres_pid, SIGINT); + } else { + gs_signal_send(pg_job_value->current_postgres_pid, SIGTERM); + } + + heap_close(pg_job_rel, NoLock); + heap_freetuple_ext(tuple); +} + +/* + * @brief stop_job_by_job_class_force + * Stop jobs by its job class. + * @param job_class_name + * @param force + */ +static void stop_job_by_job_class_force(Datum job_class_name, bool force) +{ + Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, RowExclusiveLock); + Datum job_class_attribute_name = CStringGetTextDatum("job_class"); + List *tuples = search_related_attribute(gs_job_attribute_rel, job_class_attribute_name, job_class_name); + pfree(DatumGetPointer(job_class_attribute_name)); + job_class_attribute_name = Datum(0); + if (list_length(tuples) == 0) { + heap_close(gs_job_attribute_rel, NoLock); + return; + } + ListCell *lc = NULL; + foreach(lc, tuples) { + HeapTuple tuple = (HeapTuple)lfirst(lc); + bool isnull = false; + Datum job_name = heap_getattr(tuple, Anum_gs_job_attribute_job_name, gs_job_attribute_rel->rd_att, &isnull); + stop_single_job_force(job_name, force); + } + heap_close(gs_job_attribute_rel, NoLock); + list_free_deep(tuples); +} + +/* + * @brief stop_single_job_internal + * Stop the job by sending SIGINT/SIGTERM signals to job worker. + */ +void stop_single_job_internal(PG_FUNCTION_ARGS) +{ + Datum job_name = PG_GETARG_DATUM(0); + bool force = PG_GETARG_BOOL(1); + char *object_type = get_attribute_value_str(job_name, "object_type", RowExclusiveLock, true); + if (object_type == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Undefined object %s.", TextDatumGetCString(job_name)), + errdetail("Job or job class %s does not exist", TextDatumGetCString(job_name)), + errcause("N/A"), erraction("Please check the job name."))); + } + + if (pg_strcasecmp(object_type, "job") == 0) { + stop_single_job_force(job_name, force); + } else if (pg_strcasecmp(object_type, "job_class") == 0) { + stop_job_by_job_class_force(job_name, force); + } else { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("object_type can only be job or job_class."))); + } +} + +/* + * @brief drop_single_job_name + * Drop a single job with specified job name. + * @param job_name + * @param defer + * @param force + */ +static void drop_single_job_name(Datum job_name, bool defer, bool force) +{ + check_object_type_matched(job_name, "job"); + char *job_type = get_job_type(job_name, true); + if (job_type == NULL || pg_strcasecmp(job_type, EXTERNAL_JOB_TYPE) == 0) { + check_privilege(get_role_name_str(), CREATE_EXTERNAL_JOB_PRIVILEGE); + } + pfree_ext(job_type); + + if (force && defer) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_STATUS), + errmsg("Cannot defer drop_job when 'force' is on."), + errdetail("N/A"), errcause("Conflicting condition."), + erraction("Please recheck the parameters."))); + } + bool is_running = is_job_running(job_name); + if (is_running && defer) { + /* if defer is set, expire the job immediately */ + Datum attribute_name_1 = CStringGetTextDatum("auto_drop"); + Datum attribute_value_1 = BoolToText(true); + set_job_attribute(job_name, attribute_name_1, attribute_value_1); + Datum attribute_name_2 = CStringGetTextDatum("end_date"); + Datum attribute_value_2 = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); + attribute_value_2 = DirectFunctionCall1(timestamp_text, attribute_value_2); + set_job_attribute(job_name, attribute_name_2, attribute_value_2); + return; + } + if (is_running && !force) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_INVALID_STATUS), + errmsg("Cannot drop job %s when job is running.", TextDatumGetCString(job_name)), + errdetail("Job is running."), errcause("N/A"), + erraction("Please check the job status."))); + } + if (is_running && force) { + DirectFunctionCall2((PGFunction)stop_single_job_internal, job_name, BoolGetDatum(true)); + } + drop_inline_program(job_name); + delete_from_attribute(job_name); + delete_from_job_proc(job_name); + delete_from_argument(job_name); + delete_from_job(job_name); +} + +/* + * @brief drop_scheduler_jobs_from_class + * Drop all jobs with specified job class. + */ +void drop_scheduler_jobs_from_class(Datum job_class_name, bool defer, bool force) +{ + Datum attribute_name = CStringGetTextDatum("job_class"); + Relation rel = heap_open(GsJobAttributeRelationId, AccessShareLock); + List *tuples = search_related_attribute(rel, attribute_name, job_class_name); + List *drop_job_names = NIL; + ListCell *lc = NULL; + foreach(lc, tuples) { + HeapTuple tuple = (HeapTuple)lfirst(lc); + bool isnull = false; + Datum job_name = heap_getattr(tuple, Anum_gs_job_attribute_job_name, rel->rd_att, &isnull); + Assert(!isnull); + drop_job_names = lappend(drop_job_names, DatumGetPointer(PG_DETOAST_DATUM_COPY(job_name))); + } + heap_close(rel, AccessShareLock); + + /* call disable */ + lc = NULL; + foreach(lc, drop_job_names) { + Datum job_name = (Datum)lfirst(lc); + check_object_is_visible(job_name, false); + drop_single_job_name(job_name, defer, force); + } +} + +/* + * @brief drop_job_internal + * Drop the job and all relative objects(inline program etc.) + * Note: defer and commit_semantic has no purpose yet. + */ +void drop_single_job_internal(PG_FUNCTION_ARGS) +{ + check_object_is_visible(PG_GETARG_DATUM(0), false); + Datum job_name = PG_GETARG_DATUM(0); + bool force = PG_GETARG_BOOL(1); + bool defer = PG_GETARG_BOOL(2); + + char *object_type = get_attribute_value_str(job_name, "object_type", RowExclusiveLock, true); + if (object_type == NULL) { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Undefined object %s.", TextDatumGetCString(job_name)), + errdetail("Job or job class %s does not exist", TextDatumGetCString(job_name)), + errcause("N/A"), erraction("Please check the job name."))); + } + + if (pg_strcasecmp(object_type, "job") == 0) { + drop_single_job_name(job_name, defer, force); + } else if (pg_strcasecmp(object_type, "job_class") == 0) { + drop_scheduler_jobs_from_class(job_name, defer, force); + } else { + ereport(ERROR, (errmodule(MOD_JOB), errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("name %s is %s.", TextDatumGetCString(job_name), object_type), + errdetail("N/A"), errcause("name has wrong type"), + erraction("Please check %s name", TextDatumGetCString(job_name)))); + } + return; +} + +/* + * @brief is_private_job + * Is a privatejob or not? job use log_user instead of owner, so we made a seperate function. + * @param job_name + * @param user_str + */ +static bool is_private_job(Datum job_name, const char *user_str) +{ + bool isnull = false; + Relation pg_job_rel = heap_open(PgJobRelationId, AccessShareLock); + HeapTuple tuple = search_from_pg_job(pg_job_rel, job_name); + if (!HeapTupleIsValid(tuple)) { + heap_close(pg_job_rel, AccessShareLock); + return false; + } + Datum creator = heap_getattr(tuple, Anum_pg_job_log_user, pg_job_rel->rd_att, &isnull); + char *creator_str = DatumGetCString(creator); + if (pg_strcasecmp(creator_str, user_str) == 0) { + heap_close(pg_job_rel, AccessShareLock); + return true; + } + heap_close(pg_job_rel, AccessShareLock); + return false; +} + +/* + * @brief is_private_scheduler_object + * Check if an object is fully owned by given user. + * @param rel gs_job_attribute relation + * @param object_name object name + * @param user_str given user name + * @return true is fully owned + * @return false not fully owned + */ +bool is_private_scheduler_object(Relation rel, Datum object_name, const char *user_str) +{ + bool isnull = true; + Datum attr = lookup_job_attribute(rel, object_name, CStringGetTextDatum("object_type"), &isnull, false); + char *attr_str = TextDatumGetCString(attr); + if (pg_strcasecmp(attr_str, "job") == 0) { + pfree_ext(attr_str); + if (!is_private_job(object_name, user_str)) { + return false; + } + Datum program_name = \ + lookup_job_attribute(rel, object_name, CStringGetTextDatum("program_name"), &isnull, false); + if (is_private_scheduler_object(rel, program_name, user_str)) { + return true; /* program is private */ + } + return false; + } + pfree_ext(attr_str); + Datum owner = lookup_job_attribute(rel, object_name, CStringGetTextDatum("owner"), &isnull, true); + if (owner == Datum(0)) { + /* credential does not have owner */ + return true; + } + char *owner_str = TextDatumGetCString(owner); + if (pg_strcasecmp(owner_str, user_str) != 0) { + pfree_ext(owner_str); + return false; + } + pfree_ext(owner_str); + return true; +} + +/* + * @brief disable_job_from_owner + * Disable all shared objects base on its owner. + * @param user_str usually current_user + */ +void disable_shared_job_from_owner(const char *user_str) +{ + Datum attribute_name = CStringGetTextDatum("owner"); + Datum attribute_value = CStringGetTextDatum(user_str); + Relation rel = heap_open(GsJobAttributeRelationId, AccessShareLock); + List *tuples = search_related_attribute(rel, attribute_name, attribute_value); + List *disable_job_names = NIL; + ListCell *lc = NULL; + foreach(lc, tuples) { + HeapTuple tuple = (HeapTuple)lfirst(lc); + bool isnull = false; + Datum object_name = heap_getattr(tuple, Anum_gs_job_attribute_job_name, rel->rd_att, &isnull); + Assert(!isnull); + if (is_private_scheduler_object(rel, object_name, user_str)) { + continue; + } + /* here should be all jobs, which its program is not private */ + disable_job_names = lappend(disable_job_names, DatumGetPointer(PG_DETOAST_DATUM_COPY(object_name))); + } + heap_close(rel, AccessShareLock); + + /* call disable */ + lc = NULL; + foreach(lc, disable_job_names) { + Datum program_name = (Datum)lfirst(lc); + Datum enable_value = BoolToText(false); + enable_single_force(program_name, enable_value, true); + } + list_free_deep(disable_job_names); +} + +/* + * @brief is_internal_scheduler_object + * Is object an internel object? + * @param rel + * @param object_name + * @param user_str + */ +bool is_internal_scheduler_object(Relation rel, Datum object_name) +{ + bool isnull = true; + Datum attr = lookup_job_attribute(rel, object_name, CStringGetTextDatum("object_type"), &isnull, false); + char *attr_str = TextDatumGetCString(attr); + if (pg_strcasecmp(attr_str, "job") == 0) { + pfree_ext(attr_str); + Datum program_name = \ + lookup_job_attribute(rel, object_name, CStringGetTextDatum("program_name"), &isnull, false); + if (is_internal_scheduler_object(rel, program_name)) { + return true; /* program is internal */ + } + return false; + } + pfree_ext(attr_str); + Datum program_type = lookup_job_attribute(rel, object_name, CStringGetTextDatum("program_type"), &isnull, true); + if (program_type == Datum(0)) { + return true; + } + char *program_type_str = TextDatumGetCString(program_type); + if (pg_strcasecmp(program_type_str, EXTERNAL_JOB_TYPE) == 0) { + pfree_ext(program_type_str); + return false; + } + pfree_ext(program_type_str); + return true; +} + +/* + * @brief disable_external_job_from_owner + * Disable all external objects base on its owner. + * @param user_str usually current_user + */ +void disable_external_job_from_owner(const char *user_str) +{ + Datum attribute_name = CStringGetTextDatum("owner"); + Datum attribute_value = CStringGetTextDatum(user_str); + Relation rel = heap_open(GsJobAttributeRelationId, AccessShareLock); + List *tuples = search_related_attribute(rel, attribute_name, attribute_value); + List *disable_object_names = NIL; + ListCell *lc = NULL; + foreach(lc, tuples) { + HeapTuple tuple = (HeapTuple)lfirst(lc); + bool isnull = false; + Datum object_name = heap_getattr(tuple, Anum_gs_job_attribute_job_name, rel->rd_att, &isnull); + Assert(!isnull); + if (is_internal_scheduler_object(rel, object_name)) { + continue; + } + Datum attr = lookup_job_attribute(rel, object_name, CStringGetTextDatum("object_type"), &isnull, false); + char *attr_str = TextDatumGetCString(attr); + if (pg_strcasecmp(attr_str, "job") != 0) { + pfree_ext(attr_str); + continue; /* we skip non-job for now */ + } + /* here should be a job with external program */ + disable_object_names = lappend(disable_object_names, DatumGetPointer(PG_DETOAST_DATUM_COPY(object_name))); + } + heap_close(rel, AccessShareLock); + + /* call disable */ + lc = NULL; + foreach(lc, disable_object_names) { + Datum program_name = (Datum)lfirst(lc); + Datum enable_value = BoolToText(false); + enable_single_force(program_name, enable_value, true); + } + list_free_deep(disable_object_names); +} + +/* + * @brief disable_related_job_with_program + * Disable all related jobs created by users other than 'user_str' with a given program. + * @param program_name + * @param type_str + * @param user_str + */ +static void disable_related_job_with_program(Datum program_name, const char *type_str, const char *user_str = NULL) +{ + if (pg_strcasecmp(type_str, "program") != 0) { + return; + } + + ListCell *lc = NULL; + Datum attribute_name = CStringGetTextDatum("program_name"); + Relation gs_job_attribute_rel = heap_open(GsJobAttributeRelationId, AccessShareLock); + List *disable_job_names = search_related_jobs(gs_job_attribute_rel, program_name, attribute_name, true); + heap_close(gs_job_attribute_rel, AccessShareLock); + + foreach (lc, disable_job_names) { + Datum job_name = PointerGetDatum(lfirst(lc)); + char *owner = get_attribute_value_str(job_name, "owner", AccessShareLock, false, false); + if (user_str != NULL && pg_strcasecmp(user_str, owner) == 0) { + pfree_ext(owner); + continue; /* skip current user's job */ + } + pfree_ext(owner); + update_pg_job(job_name, Anum_pg_job_enable, BoolGetDatum(false)); + } + list_free_deep(disable_job_names); + disable_job_names = NIL; +} + +/* + * @brief remove_scheduler_objects_from_owner + * Remove all external objects, including privileges from its owner. + * @param user_str + */ +void remove_scheduler_objects_from_owner(const char *user_str) +{ + Datum attribute_name = CStringGetTextDatum("owner"); + Datum attribute_value = CStringGetTextDatum(user_str); + Relation rel = heap_open(GsJobAttributeRelationId, AccessShareLock); + List *tuples = search_related_attribute(rel, attribute_name, attribute_value); + List *drop_object_names = NIL; + List *drop_object_types = NIL; + ListCell *lc = NULL; + foreach(lc, tuples) { + HeapTuple tuple = (HeapTuple)lfirst(lc); + bool isnull = false; + Datum object_name = heap_getattr(tuple, Anum_gs_job_attribute_job_name, rel->rd_att, &isnull); + Assert(!isnull); + Datum type = lookup_job_attribute(rel, object_name, CStringGetTextDatum("object_type"), &isnull, false); + drop_object_names = lappend(drop_object_names, DatumGetPointer(PG_DETOAST_DATUM_COPY(object_name))); + drop_object_types = lappend(drop_object_types, DatumGetPointer(type)); + } + heap_close(rel, AccessShareLock); + + /* call disable */ + lc = NULL; + ListCell *lc2 = NULL; + forboth(lc, drop_object_names, lc2, drop_object_types) { + Datum object_name = (Datum)lfirst(lc); + Datum object_type = (Datum)lfirst(lc2); + char *type_str = TextDatumGetCString(object_type); + check_object_type_matched(object_name, type_str); + delete_from_attribute(object_name); + delete_from_job_proc(object_name); + delete_from_argument(object_name); + delete_from_job(object_name); + + /* If it is a program, we need to disable all related jobs from other users */ + disable_related_job_with_program(object_name, type_str, user_str); + pfree(type_str); + } + const char *privilege_arr[] = {EXECUTE_ANY_PROGRAM_PRIVILEGE, CREATE_JOB_PRIVILEGE, + CREATE_EXTERNAL_JOB_PRIVILEGE, RUN_EXTERNAL_JOB_PRIVILEGE}; + int len = lengthof(privilege_arr); + for (int i = 0; i < len; i++) { + revoke_authorization(CStringGetTextDatum(user_str), CStringGetTextDatum(privilege_arr[i]), true); + } + list_free_deep(drop_object_names); + list_free_deep(drop_object_types); +} -- 2.34.1 From 27d13a7fda9b796de5e68109b89529cf8b043f5b Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:16:05 +0800 Subject: [PATCH 49/56] Delete 'src/gausskernel/process/job/job_scheduler.cpp' --- src/gausskernel/process/job/job_scheduler.cpp | 987 ------------------ 1 file changed, 987 deletions(-) delete mode 100755 src/gausskernel/process/job/job_scheduler.cpp diff --git a/src/gausskernel/process/job/job_scheduler.cpp b/src/gausskernel/process/job/job_scheduler.cpp deleted file mode 100755 index 1c5b2fb1b..000000000 --- a/src/gausskernel/process/job/job_scheduler.cpp +++ /dev/null @@ -1,987 +0,0 @@ -/* - * 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. - * ------------------------------------------------------------------------- - * - * job_scheduler.cpp - * Function for start JobScheduler thread, scan the pg_job table periodically, - * and execute the job's procedure which has expired. - * - * IDENTIFICATION - * src/gausskernel/process/job/job_scheduler.cpp - * - * ------------------------------------------------------------------------- - */ -#include "postgres.h" -#include "knl/knl_variable.h" - -#include -#include "lib/dllist.h" -#include "access/heapam.h" -#include "access/reloptions.h" -#include "access/tableam.h" -#include "access/transam.h" -#include "access/xact.h" -#include "catalog/dependency.h" -#include "catalog/namespace.h" -#include "catalog/pg_database.h" -#include "catalog/pg_authid.h" -#include "commands/dbcommands.h" -#include "commands/user.h" -#include "commands/vacuum.h" -#include "libpq/pqsignal.h" -#include "miscadmin.h" -#include "pgstat.h" -#include "pgxc/pgxc.h" -#include "postmaster/autovacuum.h" -#include "postmaster/fork_process.h" -#include "postmaster/postmaster.h" -#include "storage/buf/bufmgr.h" -#include "storage/ipc.h" -#include "storage/latch.h" -#include "storage/pmsignal.h" -#include "storage/proc.h" -#include "storage/procsignal.h" -#include "storage/sinvaladt.h" -#include "tcop/tcopprot.h" -#include "utils/fmgroids.h" -#include "utils/lsyscache.h" -#include "utils/memutils.h" -#include "utils/postinit.h" -#include "utils/ps_status.h" -#include "utils/rel.h" -#include "utils/rel_gs.h" -#include "utils/snapmgr.h" -#include "utils/syscache.h" -#include "utils/timestamp.h" -#include "access/heapam.h" -#include "catalog/pg_job.h" -#include "job/job_shmem.h" -#include "job/job_scheduler.h" -#include "gssignal/gs_signal.h" - -/* the minimum allowed time between two awakenings of the launcher */ -#define MIN_JOB_SCHEDULE_SLEEPTIME 100 /* milliseconds */ -#define MILLISECOND_PER_SECOND 1000000L /* sleep 1s when encounter with error */ -#define MILLISECOND_JOB 1000 -#define JOB_QUEUE_INTERVAL 1 /* the interval for check pg_job */ -#define UNKNOW_PID ((ThreadId)(-1)) - -/***************************************************************************** - * PRIVATE STRUCTURE DEFINE - ****************************************************************************/ -#define DLIsHead(list, elem) (DLGetHead(list) == (elem)) -#define DLIsTail(list, elem) (DLGetTail(list) == (elem)) -#define DLIsEmpty(list) ((list) == NULL || (DLGetHead(list) == NULL && DLGetTail(list) == NULL)) - -typedef struct Dlelem* DlelemPtr; -static void DLInsertByOrder(Dllist* l, Dlelem* e, int (*Comparator)(const void*, const void*)); - -/***************************************************************************** - * PRIVATE FUNCTION DEFINE - ****************************************************************************/ -static void jobschd_sighup_handler(SIGNAL_ARGS); -static void jobschd_sigusr2_handler(SIGNAL_ARGS); -static void jobschd_sigterm_handler(SIGNAL_ARGS); -static void SchedulerDetermineSleep(bool canlaunch, struct timeval* nap); -static void ScanExpireJobs(); -static int JobComparator(const void* a, const void* b); -static void ActivateWorker(); -static void check_jobinfo(); - -/***************************************************************************** - * JOB SCHEDULER IMPLEMENTS CODE : PRIVATE - ****************************************************************************/ -/* - * Description: Main loop for the job scheduler process. - * - * Parameters: - * @in argc: the number of args. - * @in argv: detail info for each args. - * Returns: void - */ -NON_EXEC_STATIC void JobScheduleMain() -{ - sigjmp_buf local_sigjmp_buf; - char username[NAMEDATALEN]; - char* dbname = (char*)pstrdup(DEFAULT_DATABASE); - - /* we are a postmaster subprocess now */ - IsUnderPostmaster = true; - t_thrd.role = JOB_SCHEDULER; - - /* reset t_thrd.proc_cxt.MyProcPid */ - t_thrd.proc_cxt.MyProcPid = gs_thread_self(); - - t_thrd.proc_cxt.MyProgName = "JobScheduler"; - u_sess->attr.attr_common.application_name = pstrdup("JobScheduler"); - - /* record Start Time for logging */ - t_thrd.proc_cxt.MyStartTime = time(NULL); - - /* Identify myself via ps */ - init_ps_display("job scheduler process", "", "", ""); - - elog(LOG, "job scheduler started"); - - SetProcessingMode(InitProcessing); - - bool isExit = IS_PGXC_COORDINATOR && IsPostmasterEnvironment; - if (isExit) { - /* - * If we exit, first try and clean connections and send to - * pooler thread does NOT exist any more, PoolerLock of LWlock is used instead. - * - * PoolManagerDisconnect() which is called by PGXCNodeCleanAndRelease() - * is the last call to pooler in the openGauss thread, and PoolerLock is - * used in PoolManagerDisconnect(), but it is called after ProcKill() - * when openGauss thread exits. - * ProcKill() releases any of its held LW locks. So Assert(!(proc == NULL ...)) - * will fail in LWLockAcquire() which is called by PoolManagerDisconnect(). - * - * All exit functions in "on_shmem_exit_list" will be called before those functions - * in "on_proc_exit_list", so move PGXCNodeCleanAndRelease() to "on_shmem_exit_list" - * and registers it after ProcKill(), and PGXCNodeCleanAndRelease() will - * be called before ProcKill(). - */ - on_shmem_exit(PGXCNodeCleanAndRelease, 0); - } - - /* - * Set up signal handlers. We operate on databases much like a regular - * backend, so we use the same signal handling. See equivalent code in - * tcop/postgres.c. - */ - (void)gspqsignal(SIGHUP, jobschd_sighup_handler); - (void)gspqsignal(SIGINT, StatementCancelHandler); - (void)gspqsignal(SIGTERM, jobschd_sigterm_handler); - - (void)gspqsignal(SIGQUIT, quickdie); - (void)gspqsignal(SIGALRM, handle_sig_alarm); - - (void)gspqsignal(SIGPIPE, SIG_IGN); - (void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); - (void)gspqsignal(SIGUSR2, jobschd_sigusr2_handler); - (void)gspqsignal(SIGFPE, FloatExceptionHandler); - (void)gspqsignal(SIGCHLD, SIG_DFL); - - if (IsUnderPostmaster) { - /* We allow SIGQUIT (quickdie) at all times */ - (void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); - } - - gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); - (void)gs_signal_unblock_sigusr2(); - - /* Early initialization */ - BaseInit(); - - /* - * Create a per-backend PGPROC struct in shared memory, except in the - * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do - * this before we can use LWLocks (and in the EXEC_BACKEND case we already - * had to do some stuff with LWLocks). - */ -#ifndef EXEC_BACKEND - InitProcess(); -#endif - - /* Initialize openGauss with DEFAULT_DATABASE, since it cannot be dropped */ - t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(dbname, InvalidOid, username); - t_thrd.proc_cxt.PostInit->InitJobScheduler(); - -#ifdef PGXC /* PGXC_COORD */ - /* - * Initialize key pair to be used as object id while using advisory lock - * for backup - */ - t_thrd.postmaster_cxt.xc_lockForBackupKey1 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_1); - t_thrd.postmaster_cxt.xc_lockForBackupKey2 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_2); -#endif - - SetProcessingMode(NormalProcessing); - - /* - * Create the memory context we will use in the main loop. - * - * t_thrd.mem_cxt.msg_mem_cxt is reset once per iteration of the main loop, ie, upon - * completion of processing of each command message from the client. - */ - t_thrd.mem_cxt.msg_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, - "MessageContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - /* - * Create a memory context that we will do all our work in. We do this so - * that we can reset the context during error recovery and thereby avoid - * possible memory leaks. - */ - t_thrd.job_cxt.JobScheduleMemCxt = AllocSetContextCreate(t_thrd.top_mem_cxt, - "Job Scheduler", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - t_thrd.job_cxt.ExpiredJobListCtx = AllocSetContextCreate(t_thrd.job_cxt.JobScheduleMemCxt, - "Expired Job List", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - /* - * If an exception is encountered, processing resumes here. - * - * This code is a stripped down version of PostgresMain error recovery. - */ - int curTryCounter; - int* oldTryCounter = NULL; - if (sigsetjmp(local_sigjmp_buf, 1) != 0) { - gstrace_tryblock_exit(true, oldTryCounter); - - /* Save error info */ - (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); - ErrorData* edata = CopyErrorData(); - - /* since not using PG_TRY, must reset error stack by hand */ - t_thrd.log_cxt.error_context_stack = NULL; - - t_thrd.log_cxt.call_stack = NULL; - - /* Prevents interrupts while cleaning up */ - HOLD_INTERRUPTS(); - - /* Forget any pending QueryCancel request */ - t_thrd.int_cxt.QueryCancelPending = false; - (void)disable_sig_alarm(true); - t_thrd.int_cxt.QueryCancelPending = false; /* again in case timeout occurred */ - - /* Report the error to the server log */ - EmitErrorReport(); - - /* Abort the current transaction in order to recover */ - AbortCurrentTransaction(); - - /* release resource held by lsc */ - AtEOXact_SysDBCache(false); - - elog(LOG, "Job scheduler encounter abnormal, detail error msg: %s.", edata->message); - - /* - * Now return to normal top-level context and clear ErrorContext for - * next time. - */ - (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); - FlushErrorState(); - - /* Flush any leaked data in the top-level context */ - MemoryContextResetAndDeleteChildren(t_thrd.job_cxt.JobScheduleMemCxt); - t_thrd.job_cxt.ExpiredJobList = NULL; - t_thrd.job_cxt.ExpiredJobListCtx = NULL; - - t_thrd.job_cxt.ExpiredJobListCtx = AllocSetContextCreate(t_thrd.job_cxt.JobScheduleMemCxt, - "Expired Job List", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - /* Now we can allow interrupts again */ - RESUME_INTERRUPTS(); - - /* - * Sleep at least 1 second after any error. We don't want to be - * filling the error logs as fast as we can. - */ - pg_usleep(MILLISECOND_PER_SECOND); - } - oldTryCounter = gstrace_tryblock_entry(&curTryCounter); - - /* We can now handle ereport(ERROR) */ - t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; - t_thrd.job_cxt.JobScheduleShmem->jsch_pid = t_thrd.proc_cxt.MyProcPid; - - /* report this backend in the PgBackendStatus array */ - u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp(); - pgstat_bestart(); - pgstat_report_appname("JobScheduler"); - pgstat_report_activity(STATE_IDLE, NULL); - - if (t_thrd.job_cxt.got_SIGTERM) { - /* Normal exit */ - ereport(LOG, (errmsg("job scheduler is shutting down"))); - - t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; - - proc_exit(0); - } - - /* - * Create a resource owner to keep track of our resources (currently only - * buffer pins). - */ - t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Job Scheduler", - THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); - - /* Get classified list of node Oids for syschronise th job status info. */ - exec_init_poolhandles(); - - (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); - - /* - * Update all the jobs's job_status from 'r' to 'f', - * may be all nodes have reseted when these jobs is under running. - */ - check_jobinfo(); - - /* Main loop */ - for (;;) { - /* close xlog file fd if any */ - CloseXlogFilesAtThreadExit(); - struct timeval nap; - TimestampTz current_time = 0; - bool can_launch = false; - int ret; - ResourceOwner save = t_thrd.utils_cxt.CurrentResourceOwner; - int4 canceled_job_id = -1; - - /* calculate sleep time, we'd like to sleep before the first launch of a child process */ - can_launch = - t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers != NULL && !DLIsEmpty(t_thrd.job_cxt.ExpiredJobList); - SchedulerDetermineSleep(can_launch, &nap); - - /* - * Wait until naptime expires or we get some type of signal (all the - * signal handlers will wake us by calling SetLatch). - */ - ret = WaitLatch(&t_thrd.proc->procLatch, - WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, - (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L)); - - ResetLatch(&t_thrd.proc->procLatch); - - /* Process sinval catchup interrupts that happened while sleeping */ - ProcessCatchupInterrupt(); - - /* - * Emergency bailout if postmaster has died. This is to avoid the - * necessity for manual cleanup of all postmaster children. - */ - if ((unsigned int)ret & WL_POSTMASTER_DEATH) { - elog(LOG, "Job scheduler shutting down with exit code 1"); - proc_exit(1); - } - - /* the normal shutdown case */ - if (t_thrd.job_cxt.got_SIGTERM) - break; - - pgstat_report_activity(STATE_RUNNING, NULL); - if (t_thrd.job_cxt.got_SIGHUP) { - t_thrd.job_cxt.got_SIGHUP = false; - (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); - ProcessConfigFile(PGC_SIGHUP); - } - - /* A job worker finished, or postmaster signalled failure to start a worker */ - if (t_thrd.job_cxt.got_SIGUSR2) { - t_thrd.job_cxt.got_SIGUSR2 = false; - - /* if postmaster fork job_worker failed, we had better to try again */ - if (t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed]) { - t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed] = false; - pg_usleep(MILLISECOND_PER_SECOND); /* sleep 1s */ - SendPostmasterSignal(PMSIGNAL_START_JOB_WORKER); - continue; - } - } - if (u_sess->attr.attr_sql.enable_prevent_job_task_startup) { - /* prevent to active job worker in config file ? */ - continue; - } - current_time = GetCurrentTimestamp(); - LWLockAcquire(JobShmemLock, LW_SHARED); - - can_launch = (t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers != NULL); - - if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker != NULL) { - int waittime; - JobWorkerInfo worker = t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker; - - /* - * We can't start another job worker when another one is still - * starting up (or failed while doing so), so just sleep for a bit - * more; that worker will wake us up again as soon as it's ready. - * We will only wait job_queue_interval seconds (up to a maximum - * of 60 seconds) for this to happen however. Note that failure - * to connect to a particular database is not a problem here, - * because the worker removes itself from the startingWorker - * pointer before trying to connect. Problems detected by the - * postmaster (like fork() failure) are also reported and handled - * differently. The only problems that may cause this code to - * fire are errors in the earlier sections of JobExecuteWorkerMain, - * before the worker removes the JobWorkerInfo from the - * startingWorker pointer. - */ - waittime = JOB_QUEUE_INTERVAL * MILLISECOND_JOB; - if (TimestampDifferenceExceeds(worker->job_launchtime, current_time, waittime)) { - LWLockRelease(JobShmemLock); - LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); - - /* - * No other process can put a worker in starting mode, so if - * startingWorker is still INVALID after exchanging our lock, - * we assume it's the same one we saw above (so we don't - * recheck the launch time). - */ - if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker != NULL) { - canceled_job_id = worker->job_id; - worker = t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker; - worker->job_dboid = InvalidOid; - worker->job_id = 0; - worker->job_launchtime = 0; - worker->job_links.next = (SHM_QUEUE*)(t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers); - t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = worker; - t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = NULL; - } - } else { - can_launch = false; - } - } - LWLockRelease(JobShmemLock); /* either shared or exclusive */ - - if (canceled_job_id > 0) { - ereport(WARNING, - (errmsg("Job worker with job id:%d took too long " - "time to start, so canceled it", - canceled_job_id))); - } - /* If we can't do anything, just go back to sleep */ - if (!can_launch || u_sess->attr.attr_sql.enable_prevent_job_task_startup) { - continue; - } - - /* Get expired job */ - if (DLIsEmpty(t_thrd.job_cxt.ExpiredJobList)) { - ScanExpireJobs(); - } - - t_thrd.utils_cxt.CurrentResourceOwner = save; - /* To start a new worker thread for execute job. */ - ActivateWorker(); - } - - /* Normal exit */ - ereport(LOG, (errmsg("job scheduler is shutting down"))); - - t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; - - proc_exit(0); -} - -/* - * Description: Receive SIGHUP and set flag to re-read config file at next convenient time. - * - * Parameters: - * @in SIGNAL_ARGS: the args of signal. - * Returns: void - */ -static void jobschd_sighup_handler(SIGNAL_ARGS) -{ - int save_errno = errno; - - t_thrd.job_cxt.got_SIGHUP = true; - if (t_thrd.proc) { - SetLatch(&t_thrd.proc->procLatch); - } - - errno = save_errno; -} - -/* - * Description: Receive SIGUSR2, a worker is up and running, or just finished, or failed to fork. - * - * Parameters: - * @in SIGNAL_ARGS: the args of signal. - * Returns: void - */ -static void jobschd_sigusr2_handler(SIGNAL_ARGS) -{ - int save_errno = errno; - elog(LOG, "Job scheduler received sigusr2 when job worker startup failed."); - - t_thrd.job_cxt.got_SIGUSR2 = true; - if (t_thrd.proc) { - SetLatch(&t_thrd.proc->procLatch); - } - - errno = save_errno; -} - -/* - * Description: Receive SIGTERM and time to die. - * - * Parameters: - * @in SIGNAL_ARGS: the args of signal. - * Returns: void - */ -static void jobschd_sigterm_handler(SIGNAL_ARGS) -{ - t_thrd.job_cxt.got_SIGTERM = true; - die(postgres_signal_arg); -} - -/* - * Description: Calculate sleep time, we'd like to sleep before the first launch of a child process. - * - * Parameters: - * @in canlaunch: there is free node in share memory for start a job worker. - * @in nap: time for sleep - * Returns: void - */ -static void SchedulerDetermineSleep(bool canlaunch, struct timeval* nap) -{ - if (!canlaunch) { - nap->tv_sec = JOB_QUEUE_INTERVAL; - nap->tv_usec = 0; - } else { - /* Sleep time should ensure the job scheduler send signal to pm to start jobworker. */ - nap->tv_sec = 0; - nap->tv_usec = MIN_JOB_SCHEDULE_SLEEPTIME * 1000; /* 0.1s */ - } -} - -/* - * Description: Insert a job element to queue order by last_start_date desc. - * - * Parameters: - * @in list: the queue of job worker - * @in newElem: job element for insert - * @in *Comparator: compare function - * Returns: void - */ -void DLInsertByOrder(Dllist* list, Dlelem* newElem, int (*Comparator)(const void* a, const void* b)) -{ - DlelemPtr elem = DLGetHead(list); - - if (NULL == elem) { - DLAddHead(list, newElem); - return; - } - - while (elem != NULL) { - if (DLIsHead(list, elem) && Comparator(elem->dle_val, newElem->dle_val) < 0) { - DLAddHead(list, newElem); - break; - } - - if (DLIsTail(list, elem) && Comparator(elem->dle_val, newElem->dle_val) > 0) { - DLAddTail(list, newElem); - break; - } - - /* Add new element next to current element. */ - if (Comparator(elem->dle_val, newElem->dle_val) > 0 && - Comparator(elem->dle_next->dle_val, newElem->dle_val) < 0) { - newElem->dle_prev = elem; - newElem->dle_next = elem->dle_next; - elem->dle_next->dle_prev = newElem; - elem->dle_next = newElem; - break; - } - - elem = DLGetSucc(elem); - } -} - -#define JOB_WORKER_RUNNING 2 -#define JOB_WORKER_STARTING 1 -#define JOB_WORKER_INACTIVE 0 - -static int GetJobStatus(int4 jobid) -{ - SHM_QUEUE* queue = NULL; - SHM_QUEUE* nextPtr = NULL; - - LWLockAcquire(JobShmemLock, LW_SHARED); - - queue = &t_thrd.job_cxt.JobScheduleShmem->jsch_runningWorkers; - nextPtr = queue; - do { - JobWorkerInfo worker = (JobWorkerInfo)nextPtr; - - if (worker->job_id == jobid) { - /* job is executing */ - LWLockRelease(JobShmemLock); - return JOB_WORKER_RUNNING; - } - nextPtr = nextPtr->next; - } while (nextPtr != queue); - - if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker && - t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker->job_id == jobid) { - /* job is ready to execute */ - LWLockRelease(JobShmemLock); - return JOB_WORKER_STARTING; - } - - LWLockRelease(JobShmemLock); - return JOB_WORKER_INACTIVE; -} - -static inline bool IsExecuteOnCurrentNode(const char* executeNodeName) -{ - if (strcmp(executeNodeName, g_instance.attr.attr_common.PGXCNodeName) == 0) - return true; - - if (strcmp(executeNodeName, PGJOB_TYPE_ALL) == 0) - return true; - - if (IS_PGXC_COORDINATOR) { - if (strcmp(executeNodeName, PGJOB_TYPE_ALL_CN) == 0) { - return true; - } else if (strcmp(executeNodeName, PGJOB_TYPE_CCN) == 0) { - return is_pgxc_central_nodename(g_instance.attr.attr_common.PGXCNodeName); - } else { - return false; - } - } - - if (IS_PGXC_DATANODE && strcmp(executeNodeName, PGJOB_TYPE_ALL_DN) == 0) - return true; - - return false; -} - -/* - * @brief SkipSchedulerJob - * Skip process DBE_SCHEDULER jobs, contains various checks. - * 1. whether the job is enabled - * 2. whether the job is expired(end_date < current timestamp OR end_date is NULL) - * @param values pg_job attr values - * @param nulls pg_job attr nulls - * @return true skip current job - * @return false do not skip current job - */ -static bool SkipSchedulerJob(Datum *values, bool *nulls, Timestamp curtime) -{ - Assert(values != NULL); - Assert(nulls != NULL); - /* do not handle non-scheduler jobs */ - if (nulls[Anum_pg_job_job_name]) { - return false; - } - - /* expired job, need to drop even it is disabled */ - if (DatumGetBool(DirectFunctionCall2(timestamp_ge, curtime, values[Anum_pg_job_end_date - 1]))) { - return false; - } - - /* disabled jobs */ - if (!nulls[Anum_pg_job_enable - 1] && !DatumGetBool(values[Anum_pg_job_enable - 1])) { - return true; /* skip here to avoid further overhead */ - } - - return false; -} - -/* - * Description: Find expire jobs and insert to job queue for execute. - * - * Returns: void - */ -static void ScanExpireJobs() -{ - Relation pg_job_tbl = NULL; - TableScanDesc scan = NULL; - HeapTuple tuple = NULL; - MemoryContext oldCtx = NULL; - int jobStatus = JOB_WORKER_INACTIVE; - Datum curtime = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); - - StartTransactionCommand(); - - pg_job_tbl = heap_open(PgJobRelationId, AccessShareLock); - scan = tableam_scan_begin(pg_job_tbl, SnapshotNow, 0, NULL); - - MemoryContextReset(t_thrd.job_cxt.ExpiredJobListCtx); - oldCtx = MemoryContextSwitchTo(t_thrd.job_cxt.ExpiredJobListCtx); - /* Build a new job list if it is null. */ - t_thrd.job_cxt.ExpiredJobList = DLNewList(); - while (HeapTupleIsValid(tuple = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection))) { - Form_pg_job pg_job = (Form_pg_job)GETSTRUCT(tuple); - Datum values[Natts_pg_job]; - bool nulls[Natts_pg_job]; - char status = pg_job->job_status; - int64 jobID = pg_job->job_id; - - get_job_values(jobID, tuple, pg_job_tbl, values, nulls); - - /* dbms schedule creates a job but dont enable it */ - if (SkipSchedulerJob(values, nulls, curtime)) { - continue; - } - - /* handle cases - ALL_NODE/ALL_CN/ALL_DN/CCN specific node */ - if (!IsExecuteOnCurrentNode(pg_job->node_name.data)) { - continue; - } - - if (false == DatumGetBool(DirectFunctionCall2(timestamp_gt, curtime, values[Anum_pg_job_next_run_date - 1]))) { - /* skip since it doesnot reach book time */ - continue; - } - - jobStatus = GetJobStatus(jobID); - if (PGJOB_RUN_STATUS == status) { - if (JOB_WORKER_INACTIVE != jobStatus) { - /* skip since job is active */ - continue; - } - - /* ready to execute the job since it is not on executing */ - } else if (PGJOB_ABORT_STATUS == status) { - /* skip since the job is broken */ - continue; - } else { - /* ready to execute the job */ - Assert(PGJOB_FAIL_STATUS == pg_job->job_status || PGJOB_SUCC_STATUS == pg_job->job_status); - - if (JOB_WORKER_RUNNING == jobStatus) { - /* - * 1. skip long time job check since job will be finished soon - * 2. skip do the job since job is running - */ - continue; - } else if (JOB_WORKER_STARTING == jobStatus) { - ereport(WARNING, (errmsg("[job id %ld] worker is in risk of startup timeout", jobID))); - /* skip since job woker is starting */ - continue; - } else { - Assert(JOB_WORKER_INACTIVE == jobStatus); - /* ready to execute the job since it is not on executing */ - } - } - - Oid dboid = get_database_oid(NameStr(pg_job->dbname), true); - if (!OidIsValid(dboid)) { - /* skip since the database of job does not exist */ - ereport(LOG, (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%s\" of job %ld does not exist", NameStr(pg_job->dbname), jobID))); - continue; - } - - JobInfo jobInfo = (JobInfoData*)palloc0(sizeof(JobInfoData)); - jobInfo->job_id = jobID; - jobInfo->job_oid = HeapTupleGetOid(tuple); - jobInfo->job_dboid = dboid; - jobInfo->log_user = pg_job->log_user; - jobInfo->node_name = pg_job->node_name; - jobInfo->last_start_date = - (nulls[Anum_pg_job_last_start_date - 1] ? 0 : values[Anum_pg_job_last_start_date - 1]); - DLInsertByOrder(t_thrd.job_cxt.ExpiredJobList, DLNewElem(jobInfo), JobComparator); - } - - (void)MemoryContextSwitchTo(oldCtx); - tableam_scan_end(scan); - heap_close(pg_job_tbl, AccessShareLock); - - CommitTransactionCommand(); -} - -/* - * Description: Compare with last_start_date and decide the smaller will insert previes. - * - * Parameters: - * @in baseOne: base job - * @in newOne: new job - * Returns: int - */ -static int JobComparator(const void* baseOne, const void* newOne) -{ - if (((const JobInfo)newOne)->last_start_date <= ((const JobInfo)baseOne)->last_start_date) { - return -1; - } else { - return 1; - } -} - -/* - * Description: Send SIGUSR2 to postmaster and start a new job worker. - * - * Returns: void - */ -static void ActivateWorker() -{ - JobWorkerInfo worker = NULL; - JobInfo jobInfo = NULL; - DlelemPtr head_job = NULL; - - if (DLIsEmpty(t_thrd.job_cxt.ExpiredJobList)) { - /* return immediately, after a period, main loop will fetch jobs again */ - return; - } - - /* return quickly when there are no free job workers */ - LWLockAcquire(JobShmemLock, LW_SHARED); - worker = t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers; - if (NULL == worker) { - LWLockRelease(JobShmemLock); - ereport(LOG, (errmsg("skip to activate job since no free job workers."))); - return; - } - LWLockRelease(JobShmemLock); - - /* remove and return the head job from ExpiredJobList */ - head_job = DLRemHead(t_thrd.job_cxt.ExpiredJobList); - if (NULL == head_job || NULL == head_job->dle_val) { - /* - * just throw an error if ExpiredJobList is invalid., and execution - * environment of the scheduler will be reset in function - * JobScheduleMain - */ - ereport(ERROR, - ((errcode(ERRCODE_INVALID_STATUS), - errmsg("refuse to activate a job since illegal element in ExpiredJobList.")))); - } - jobInfo = (JobInfo)(head_job->dle_val); - - LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); - - /* Get a worker from freelist, and start it */ - worker = t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers; - if (NULL == worker) { - LWLockRelease(JobShmemLock); - /* log error, and proc exit */ - ereport(FATAL, (errmsg("no free slot when start job worker"))); - return; - } - - t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = (JobWorkerInfo)worker->job_links.next; - - worker->job_dboid = jobInfo->job_dboid; - worker->job_id = jobInfo->job_id; - worker->job_oid = jobInfo->job_oid; - worker->username = jobInfo->log_user; - worker->job_launchtime = GetCurrentTimestamp(); - worker->job_worker_pid = UNKNOW_PID; - - t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = worker; - - LWLockRelease(JobShmemLock); - - /* Tell postmaster start a new job worker. */ - SendPostmasterSignal(PMSIGNAL_START_JOB_WORKER); - DLFreeElem(head_job); - - elog(LOG, "Job scheduler send signal to postmaster to start job worker, jobid=%d.", worker->job_id); -} - -/* - * Description: Check job's status is 'r' and update to 'f' when start job scheduler thread. - * - * Returns: void - */ -static void check_jobinfo() -{ - ResourceOwner save = t_thrd.utils_cxt.CurrentResourceOwner; - - StartTransactionCommand(); - (void)GetTransactionSnapshot(); - - PG_TRY(); - { - /* Check if have job which status is 'r', and update job_status as 'f'. */ - update_run_job_to_fail(); - CommitTransactionCommand(); - } - PG_CATCH(); - { - FlushErrorState(); - elog(LOG, "Check job info failed"); - AbortCurrentTransaction(); - } - PG_END_TRY(); - - t_thrd.utils_cxt.CurrentResourceOwner = save; -} - -/* - * Description: Shared memory size. - * - * Returns: Size - */ -Size JobInfoShmemSize(void) -{ - Size size; - - /* Need the fixed struct and the array of JobWorkerInfoData */ - size = sizeof(JobScheduleShmemStruct); - size = MAXALIGN(size); - size = add_size(size, mul_size(g_instance.attr.attr_sql.job_queue_processes, sizeof(JobWorkerInfoData))); - return size; -} - -/* - * Description: Init shared memory. - * - * Returns: void - */ -void JobInfoShmemInit(void) -{ - bool found = false; - t_thrd.job_cxt.JobScheduleShmem = - (JobScheduleShmemStruct*)ShmemInitStruct("Job Scheduler Data", JobInfoShmemSize(), &found); - - if (!IsUnderPostmaster) { - JobWorkerInfo worker; - - AssertEreport(!found, MOD_EXECUTOR, ""); - - t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; - t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = NULL; - SHMQueueInit(&t_thrd.job_cxt.JobScheduleShmem->jsch_runningWorkers); - t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = NULL; - - worker = (JobWorkerInfo)((char*)t_thrd.job_cxt.JobScheduleShmem + MAXALIGN(sizeof(JobScheduleShmemStruct))); - - /* Create new freeworker queue. */ - for (int i = 0; i < g_instance.attr.attr_sql.job_queue_processes; ++i) { - worker[i].job_links.next = (SHM_QUEUE*)(t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers); - t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = &worker[i]; - } - } else { - AssertEreport(found, MOD_EXECUTOR, ""); - } -} - -/* - * Description: return true if the thread is job scheduler. - * - * Returns: bool - */ -bool IsJobSchedulerProcess(void) -{ - return t_thrd.role == JOB_SCHEDULER; -} - -/* - * RecordForkJobWorkerFailed: Called from postmaster when a worker could not be forked. - * - * Returns: void - */ -void RecordForkJobWorkerFailed(void) -{ - t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed] = true; -} -- 2.34.1 From d3b89834a61fe0ca270496f8dbfe49c57f14544c Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:16:20 +0800 Subject: [PATCH 50/56] ADD file via upload --- src/gausskernel/process/job/job_scheduler.cpp | 1070 +++++++++++++++++ 1 file changed, 1070 insertions(+) create mode 100644 src/gausskernel/process/job/job_scheduler.cpp diff --git a/src/gausskernel/process/job/job_scheduler.cpp b/src/gausskernel/process/job/job_scheduler.cpp new file mode 100644 index 000000000..6e129dd10 --- /dev/null +++ b/src/gausskernel/process/job/job_scheduler.cpp @@ -0,0 +1,1070 @@ +/* + * 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. + * ------------------------------------------------------------------------- + * + * job_scheduler.cpp + * Function for start JobScheduler thread, scan the pg_job table periodically, + * and execute the job's procedure which has expired. + * + * IDENTIFICATION + * src/gausskernel/process/job/job_scheduler.cpp + * + * ------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "knl/knl_variable.h" + +#include +#include "lib/dllist.h" +#include "access/heapam.h" +#include "access/reloptions.h" +#include "access/tableam.h" +#include "access/transam.h" +#include "access/xact.h" +#include "catalog/dependency.h" +#include "catalog/namespace.h" +#include "catalog/pg_database.h" +#include "catalog/pg_authid.h" +#include "commands/dbcommands.h" +#include "commands/user.h" +#include "commands/vacuum.h" +#include "libpq/pqsignal.h" +#include "miscadmin.h" +#include "pgstat.h" +#include "pgxc/pgxc.h" +#include "postmaster/autovacuum.h" +#include "postmaster/fork_process.h" +#include "postmaster/postmaster.h" +#include "storage/buf/bufmgr.h" +#include "storage/ipc.h" +#include "storage/latch.h" +#include "storage/pmsignal.h" +#include "storage/proc.h" +#include "storage/procsignal.h" +#include "storage/sinvaladt.h" +#include "tcop/tcopprot.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/postinit.h" +#include "utils/ps_status.h" +#include "utils/rel.h" +#include "utils/rel_gs.h" +#include "utils/snapmgr.h" +#include "utils/syscache.h" +#include "utils/timestamp.h" +#include "access/heapam.h" +#include "catalog/pg_job.h" +#include "job/job_shmem.h" +#include "job/job_scheduler.h" +#include "gssignal/gs_signal.h" + +/* the minimum allowed time between two awakenings of the launcher */ +#define MIN_JOB_SCHEDULE_SLEEPTIME 100 /* milliseconds *///运行器唤醒的最小允许时间间隔,单位为毫秒。 +#define MILLISECOND_PER_SECOND 1000000L /* sleep 1s when encounter with error *///遇到错误时休眠1秒的时间,单位为微秒。 +#define MILLISECOND_JOB 1000//作业执行的时间,单位为毫秒。 +#define JOB_QUEUE_INTERVAL 1 /* the interval for check pg_job *///检查pg_job的间隔时间,单位为秒。 +#define UNKNOW_PID ((ThreadId)(-1))//未知进程ID。 + +/***************************************************************************** + * PRIVATE STRUCTURE DEFINE + ****************************************************************************/ +#define DLIsHead(list, elem) (DLGetHead(list) == (elem)) +#define DLIsTail(list, elem) (DLGetTail(list) == (elem)) +#define DLIsEmpty(list) ((list) == NULL || (DLGetHead(list) == NULL && DLGetTail(list) == NULL)) + +typedef struct Dlelem* DlelemPtr;//指向Dlelem结构体的指针。 +static void DLInsertByOrder(Dllist* l, Dlelem* e, int (*Comparator)(const void*, const void*)); + +/***************************************************************************** + * PRIVATE FUNCTION DEFINE + ****************************************************************************/ +static void jobschd_sighup_handler(SIGNAL_ARGS);//SIGHUP信号处理函数。 +static void jobschd_sigusr2_handler(SIGNAL_ARGS);//SIGUSR2信号处理函数。 +static void jobschd_sigterm_handler(SIGNAL_ARGS);//SIGTERM信号处理函数。 +static void SchedulerDetermineSleep(bool canlaunch, struct timeval* nap);//根据条件决定运行器的休眠时间。 +static void ScanExpireJobs();//扫描过期的作业。 +static int JobComparator(const void* a, const void* b);//作业比较函数。 +static void ActivateWorker();//激活工作线程。 +static void check_jobinfo();//检查作业信息。 + +/***************************************************************************** + * JOB SCHEDULER IMPLEMENTS CODE : PRIVATE + ****************************************************************************/ +/* + * Description: Main loop for the job scheduler process. + * + * Parameters: + * @in argc: the number of args. + * @in argv: detail info for each args. + * Returns: void + */ +NON_EXEC_STATIC void JobScheduleMain() +{ + sigjmp_buf local_sigjmp_buf; // 保存信号跳转信息的变量 + char username[NAMEDATALEN]; // 存储用户名的数组 + char* dbname = (char*)pstrdup(DEFAULT_DATABASE);// 存储默认数据库名称的指针 + + /* we are a postmaster subprocess now */ // 我们现在是一个后台进程 + IsUnderPostmaster = true; + t_thrd.role = JOB_SCHEDULER; // 线程角色设置为JOB_SCHEDULER表示正在执行作业调度器的功能 + + /* reset t_thrd.proc_cxt.MyProcPid */ // 重置当前进程的进程ID为当前线程的ID + t_thrd.proc_cxt.MyProcPid = gs_thread_self(); + + t_thrd.proc_cxt.MyProgName = "JobScheduler"; // 当前进程的程序名称设置为"JobScheduler" + u_sess->attr.attr_common.application_name = pstrdup("JobScheduler"); // 当前会话的应用程序名称设置为"JobScheduler" + + /* record Start Time for logging */ // 记录当前进程的启动时间 + t_thrd.proc_cxt.MyStartTime = time(NULL); + + /* Identify myself via ps */ // 通过ps命令显示当前进程的信息 + init_ps_display("job scheduler process", "", "", ""); + + elog(LOG, "job scheduler started"); // 在日志中记录作业调度器已启动 + + SetProcessingMode(InitProcessing); // 将处理模式设置为初始化模式 + + bool isExit = IS_PGXC_COORDINATOR && IsPostmasterEnvironment; + if (isExit) {//布尔变量isExit,判断条件是当前环境为PGXC协调器并且处于Postmaster环境 + /* + * If we exit, first try and clean connections and send to + * pooler thread does NOT exist any more, PoolerLock of LWlock is used instead. + * + * PoolManagerDisconnect() which is called by PGXCNodeCleanAndRelease() + * is the last call to pooler in the openGauss thread, and PoolerLock is + * used in PoolManagerDisconnect(), but it is called after ProcKill() + * when openGauss thread exits. + * ProcKill() releases any of its held LW locks. So Assert(!(proc == NULL ...)) + * will fail in LWLockAcquire() which is called by PoolManagerDisconnect(). + * + * All exit functions in "on_shmem_exit_list" will be called before those functions + * in "on_proc_exit_list", so move PGXCNodeCleanAndRelease() to "on_shmem_exit_list" + * and registers it after ProcKill(), and PGXCNodeCleanAndRelease() will + * be called before ProcKill(). + */ + on_shmem_exit(PGXCNodeCleanAndRelease, 0);//注册到"on_shmem_exit_list"后的函数,将在进程退出时被调用 + } + + /* + * Set up signal handlers. We operate on databases much like a regular + * backend, so we use the same signal handling. See equivalent code in + * tcop/postgres.c. + */ + /* 设置SIGHUP信号处理函数为jobschd_sighup_handler */ + (void)gspqsignal(SIGHUP, jobschd_sighup_handler); + + /* 设置SIGINT信号处理函数为StatementCancelHandler */ + (void)gspqsignal(SIGINT, StatementCancelHandler); + + /* 设置SIGTERM信号处理函数为jobschd_sigterm_handler */ + (void)gspqsignal(SIGTERM, jobschd_sigterm_handler); + + /* 设置SIGQUIT信号处理函数为quickdie */ + (void)gspqsignal(SIGQUIT, quickdie); + + /* 设置SIGALRM信号处理函数为handle_sig_alarm */ + (void)gspqsignal(SIGALRM, handle_sig_alarm); + + /* 忽略SIGPIPE信号 */ + (void)gspqsignal(SIGPIPE, SIG_IGN); + + /* 设置SIGUSR1信号处理函数为procsignal_sigusr1_handler */ + (void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); + + /* 设置SIGUSR2信号处理函数为jobschd_sigusr2_handler */ + (void)gspqsignal(SIGUSR2, jobschd_sigusr2_handler); + + /* 设置SIGFPE信号处理函数为FloatExceptionHandler */ + (void)gspqsignal(SIGFPE, FloatExceptionHandler); + + /* SIGCHLD信号使用默认处理方式 */ + (void)gspqsignal(SIGCHLD, SIG_DFL); + + /* 如果在Postmaster进程之下,允许使用SIGQUIT (quickdie)信号 */ + if (IsUnderPostmaster) { + (void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); + } + + /* 设置阻塞信号集为t_thrd.libpq_cxt.UnBlockSig */ + gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); + + /* 解除对SIGUSR2信号的阻塞 */ + (void)gs_signal_unblock_sigusr2(); + + /* 早期初始化 */ + BaseInit(); + + + /* + * Create a per-backend PGPROC struct in shared memory, except in the + * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do + * this before we can use LWLocks (and in the EXEC_BACKEND case we already + * had to do some stuff with LWLocks). + */ + #ifndef EXEC_BACKEND + InitProcess(); + #endif + + /* 初始化进程 */ + // 如果没有定义EXEC_BACKEND宏,则调用InitProcess()函数进行进程初始化的工作 + + /* 使用DEFAULT_DATABASE初始化openGauss,因为无法删除它 */ + t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(dbname, InvalidOid, username); + t_thrd.proc_cxt.PostInit->InitJobScheduler(); + + #ifdef PGXC /* PGXC_COORD */ + /* + * 为备份时使用的咨询锁初始化键对。 + */ + t_thrd.postmaster_cxt.xc_lockForBackupKey1 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_1); + t_thrd.postmaster_cxt.xc_lockForBackupKey2 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_2); + #endif + + /* 设置处理模式为NormalProcessing */ + SetProcessingMode(NormalProcessing); + + /* + * 创建主循环中将使用的内存上下文。 + * + * t_thrd.mem_cxt.msg_mem_cxt在每次主循环迭代(即完成对客户端的每个命令消息处理后)时重置一次。 + */ + t_thrd.mem_cxt.msg_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, + "MessageContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + /* + * 创建一个内存上下文,我们将在其中执行所有工作。 + * 我们这样做是为了在错误恢复期间重置上下文,从而避免可能的内存泄漏。 + */ + t_thrd.job_cxt.JobScheduleMemCxt = AllocSetContextCreate(t_thrd.top_mem_cxt, + "Job Scheduler", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + t_thrd.job_cxt.ExpiredJobListCtx = AllocSetContextCreate(t_thrd.job_cxt.JobScheduleMemCxt, + "Expired Job List", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + + /* + * 如果遇到异常,处理将从这里恢复。 + * + * 这段代码是PostgresMain错误恢复的简化版本。 + */ + int curTryCounter; + int* oldTryCounter = NULL; + + /* 使用sigsetjmp函数设置跳转点,并检查返回值以确定是否从跳转点返回 */ + if (sigsetjmp(local_sigjmp_buf, 1) != 0) { + gstrace_tryblock_exit(true, oldTryCounter); + + /* 保存错误信息 */ + (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); + ErrorData* edata = CopyErrorData(); + + /* 由于没有使用PG_TRY,需要手动重置错误堆栈 */ + t_thrd.log_cxt.error_context_stack = NULL; + t_thrd.log_cxt.call_stack = NULL; + + /* 清理时防止中断 */ + HOLD_INTERRUPTS(); + + /* 取消任何待处理的QueryCancel请求 */ + t_thrd.int_cxt.QueryCancelPending = false; + (void)disable_sig_alarm(true); + t_thrd.int_cxt.QueryCancelPending = false; /* 再次取消,以防超时发生 */ + + /* 将错误报告记录到服务器日志 */ + EmitErrorReport(); + + /* 中止当前事务以进行恢复 */ + AbortCurrentTransaction(); + + /* 释放lsc持有的资源 */ + AtEOXact_SysDBCache(false); + + elog(LOG, "Job scheduler encounter abnormal, detail error msg: %s.", edata->message); + + /* + * 现在回到正常的顶层上下文,并清除ErrorContext以供下次使用。 + */ + (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); + FlushErrorState(); + + /* 刷新顶层上下文中的任何泄漏的数据 */ + MemoryContextResetAndDeleteChildren(t_thrd.job_cxt.JobScheduleMemCxt); + t_thrd.job_cxt.ExpiredJobList = NULL; + t_thrd.job_cxt.ExpiredJobListCtx = NULL; + + t_thrd.job_cxt.ExpiredJobListCtx = AllocSetContextCreate(t_thrd.job_cxt.JobScheduleMemCxt, + "Expired Job List", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + /* 现在可以再次允许中断了 */ + RESUME_INTERRUPTS(); + + /* + * 在发生错误后至少休眠1秒。 + * 我们不希望错误日志文件被填满。 + */ + pg_usleep(MILLISECOND_PER_SECOND); + } + + oldTryCounter = gstrace_tryblock_entry(&curTryCounter); // 记录当前try块被调用的次数 + + /* 设置异常处理跳转点 */ + t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; + t_thrd.job_cxt.JobScheduleShmem->jsch_pid = t_thrd.proc_cxt.MyProcPid; // 在JobScheduler共享内存中设置进程ID + + /* 在PgBackendStatus数组中报告该后台进程 */ + u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp(); // 设置会话开始时间 + pgstat_bestart(); // 开始统计信息收集 + pgstat_report_appname("JobScheduler"); // 报告应用程序名称为"JobScheduler" + pgstat_report_activity(STATE_IDLE, NULL); // 报告活动状态为空闲 + + if (t_thrd.job_cxt.got_SIGTERM) { + /* 正常退出 */ + ereport(LOG, (errmsg("job scheduler is shutting down"))); + + t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; // 将JobScheduler共享内存中的进程ID设置为0 + + proc_exit(0); // 进程退出 + } + + /* + * 创建资源所有者以跟踪资源(目前只有缓冲区引用)。 + */ + t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Job Scheduler", + THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); // 创建当前资源所有者,并命名为"Job Scheduler" + + /* 获取分类的节点OID列表,用于同步作业状态信息 */ + exec_init_poolhandles(); // 初始化节点的连接句柄 + + (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); // 切换到JobScheduler内存上下文 + + /* + * 将所有作业的作业状态从'r'更新为'f', + * 当这些作业正在运行时,可能所有节点都已重置。 + */ + check_jobinfo(); // 检查作业信息 + +//------------------------------------------------------------------------------------------------------------------------------ + /* 主循环 */ + for (;;) { + /* 关闭已存在的xlog文件句柄 */ + CloseXlogFilesAtThreadExit(); + struct timeval nap; + TimestampTz current_time = 0; + bool can_launch = false; + int ret; + ResourceOwner save = t_thrd.utils_cxt.CurrentResourceOwner; + int4 canceled_job_id = -1; + + /* 计算睡眠时间,在启动子进程前我们希望先休眠一段时间 */ + can_launch = + t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers != NULL && !DLIsEmpty(t_thrd.job_cxt.ExpiredJobList); + SchedulerDetermineSleep(can_launch, &nap); + + /* + * 等待直到naptime过期或者接收到信号(所有信号处理程序会调用SetLatch唤醒我们)。 + */ + ret = WaitLatch(&t_thrd.proc->procLatch, + WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, + (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L)); + + ResetLatch(&t_thrd.proc->procLatch); + + /* 处理在休眠期间发生的sinval追赶中断 */ + ProcessCatchupInterrupt(); + + /* + * 如果postmaster已经终止,紧急退出。这是为了避免手动清理所有postmaster子进程的必要性。 + */ + if ((unsigned int)ret & WL_POSTMASTER_DEATH) { + elog(LOG, "Job scheduler shutting down with exit code 1"); + proc_exit(1); + } + + /* 正常的关闭情况 */ + if (t_thrd.job_cxt.got_SIGTERM) + break; + + pgstat_report_activity(STATE_RUNNING, NULL); + if (t_thrd.job_cxt.got_SIGHUP) { + t_thrd.job_cxt.got_SIGHUP = false; + (void)MemoryContextSwitchTo(t_thrd.job_cxt.JobScheduleMemCxt); + ProcessConfigFile(PGC_SIGHUP); + } + + /* 一个作业工作者已经完成,或者postmaster发出启动工作者失败的信号 */ + if (t_thrd.job_cxt.got_SIGUSR2) { + t_thrd.job_cxt.got_SIGUSR2 = false; + + /* 如果postmaster fork job_worker失败,最好尝试重新启动 */ + if (t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed]) { + t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed] = false; + pg_usleep(MILLISECOND_PER_SECOND); /* 休眠1秒 */ + SendPostmasterSignal(PMSIGNAL_START_JOB_WORKER); + continue; + } + } + if (u_sess->attr.attr_sql.enable_prevent_job_task_startup) { + /* 在配置文件中禁止激活作业工作者吗? */ + continue; + } + current_time = GetCurrentTimestamp(); + LWLockAcquire(JobShmemLock, LW_SHARED); + + can_launch = (t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers != NULL); + + if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker != NULL) { + int waittime; + JobWorkerInfo worker = t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker; + + /* + * 当另一个工作者正在启动(或者在启动过程中失败)时,我们不能启动另一个作业工作者,所以稍微休眠一会儿; + * 当他准备好后,他会再次唤醒我们。不过我们只会等待job_queue_interval秒钟(最多60秒)。 + * 请注意,连接到特定数据库失败不是问题,因为工作者在尝试连接之前会从startingWorker指针中删除自己。 + * 由postmaster检测到的问题(例如fork()失败)会以不同的方式报告和处理。 + * 只有在JobExecuteWorkerMain的早期部分发生错误时,此代码才会触发,即在工作者将JobWorkerInfo从startingWorker指针中删除之前。 + */ + waittime = JOB_QUEUE_INTERVAL * MILLISECOND_JOB; + if (TimestampDifferenceExceeds(worker->job_launchtime, current_time, waittime)) { + LWLockRelease(JobShmemLock); + LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); + + /* + * 在我们获得锁之后,没有其他进程可以将工作者置于启动模式, + * 所以如果在交换锁之后startingWorker仍然无效,我们认为它与上面看到的相同(因此我们不重新检查启动时间)。 + */ + if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker != NULL) { + canceled_job_id = worker->job_id; + worker = t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker; + worker->job_dboid = InvalidOid; + worker->job_id = 0; + worker->job_launchtime = 0; + worker->job_links.next = (SHM_QUEUE*)(t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers); + t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = worker; + t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = NULL; + } + } else { + can_launch = false; + } + } + LWLockRelease(JobShmemLock); /* 释放共享锁或独占锁 */ + + if (canceled_job_id > 0) { + ereport(WARNING, + (errmsg("Job worker with job id:%d took too long " + "time to start, so canceled it", + canceled_job_id))); + } + /* 如果我们无法做任何事情,就继续休眠 */ + if (!can_launch || u_sess->attr.attr_sql.enable_prevent_job_task_startup) { + continue; + } + + /* 获取到期的作业 */ + if (DLIsEmpty(t_thrd.job_cxt.ExpiredJobList)) { + ScanExpireJobs(); + } + + t_thrd.utils_cxt.CurrentResourceOwner = save; + /* 启动一个新的工作者线程来执行作业 */ + ActivateWorker(); + } + + /* 正常退出 */ + ereport(LOG, (errmsg("job scheduler is shutting down"))); + + t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; + + proc_exit(0); +} +//------------------------------------------------------------------------------------------------------------------------------ +/* + * Description: Receive SIGHUP and set flag to re-read config file at next convenient time. + * + * Parameters: + * @in SIGNAL_ARGS: the args of signal. + * Returns: void + */ +/* SIGHUP信号处理程序 */ +static void jobschd_sighup_handler(SIGNAL_ARGS) +{ + int save_errno = errno; + + /* 设置标志,表示接收到SIGHUP信号 */ + t_thrd.job_cxt.got_SIGHUP = true; + + /* 如果当前进程存在,则设置进程的Latch,以便唤醒进程 */ + if (t_thrd.proc) { + SetLatch(&t_thrd.proc->procLatch); + } + + errno = save_errno; +} + +/* + * Description: Receive SIGUSR2, a worker is up and running, or just finished, or failed to fork. + * + * Parameters: + * @in SIGNAL_ARGS: the args of signal. + * Returns: void + */ +/* SIGUSR2信号处理程序 */ +static void jobschd_sigusr2_handler(SIGNAL_ARGS) +{ + int save_errno = errno; + + /* 记录日志,表示接收到SIGUSR2信号并且作业工作者启动失败 */ + elog(LOG, "Job scheduler received sigusr2 when job worker startup failed."); + + /* 设置标志,表示接收到SIGUSR2信号 */ + t_thrd.job_cxt.got_SIGUSR2 = true; + + /* 如果当前进程存在,则设置进程的Latch,以便唤醒进程 */ + if (t_thrd.proc) { + SetLatch(&t_thrd.proc->procLatch); + } + + errno = save_errno; +} + +/* + * Description: Receive SIGTERM and time to die. + * + * Parameters: + * @in SIGNAL_ARGS: the args of signal. + * Returns: void + */ + /* sigterm信号处理程序 */ +static void jobschd_sigterm_handler(SIGNAL_ARGS) +{ + /* 设置标志,表示接收到SIGTERM信号 */ + t_thrd.job_cxt.got_SIGTERM = true; + + /* 退出进程,并传递给die函数的信号参数 */ + die(postgres_signal_arg); +} + +/* + * Description: Calculate sleep time, we'd like to sleep before the first launch of a child process. + * + * Parameters: + * @in canlaunch: there is free node in share memory for start a job worker. + * @in nap: time for sleep + * Returns: void + */ +static void SchedulerDetermineSleep(bool canlaunch, struct timeval* nap) +{ + if (!canlaunch) { + /* 如果无法启动作业工作者,则休眠 JOB_QUEUE_INTERVAL 秒 */ + nap->tv_sec = JOB_QUEUE_INTERVAL; + nap->tv_usec = 0; + } else { + /* 否则,休眠时间应该足够长,以确保作业调度程序发送信号给 postmaster 启动作业工作者 */ + nap->tv_sec = 0; + nap->tv_usec = MIN_JOB_SCHEDULE_SLEEPTIME * 1000; /* 0.1秒 */ + } +} + +/* + * Description: Insert a job element to queue order by last_start_date desc. + * + * Parameters: + * @in list: the queue of job worker + * @in newElem: job element for insert + * @in *Comparator: compare function + * Returns: void + */ +void DLInsertByOrder(Dllist* list, Dlelem* newElem, int (*Comparator)(const void* a, const void* b)) +{ + DlelemPtr elem = DLGetHead(list); + + if (NULL == elem) { + /* 如果链表为空,则将新元素添加到链表头部 */ + DLAddHead(list, newElem); + return; + } + + while (elem != NULL) { + if (DLIsHead(list, elem) && Comparator(elem->dle_val, newElem->dle_val) < 0) { + /* 如果当前元素是链表头部元素,并且比新元素小,则将新元素插入到链表头部 */ + DLAddHead(list, newElem); + break; + } + + if (DLIsTail(list, elem) && Comparator(elem->dle_val, newElem->dle_val) > 0) { + /* 如果当前元素是链表尾部元素,并且比新元素大,则将新元素插入到链表尾部 */ + DLAddTail(list, newElem); + break; + } + + /* 将新元素插入到当前元素的下一个位置 */ + if (Comparator(elem->dle_val, newElem->dle_val) > 0 && + Comparator(elem->dle_next->dle_val, newElem->dle_val) < 0) { + newElem->dle_prev = elem; + newElem->dle_next = elem->dle_next; + elem->dle_next->dle_prev = newElem; + elem->dle_next = newElem; + break; + } + + elem = DLGetSucc(elem); + } +} +#define JOB_WORKER_RUNNING 2 +#define JOB_WORKER_STARTING 1 +#define JOB_WORKER_INACTIVE 0 + +/* 获取作业状态的函数 */ +static int GetJobStatus(int4 jobid) +{ + SHM_QUEUE* queue = NULL; + SHM_QUEUE* nextPtr = NULL; + + /* 共享内存锁定 */ + LWLockAcquire(JobShmemLock, LW_SHARED); + + queue = &t_thrd.job_cxt.JobScheduleShmem->jsch_runningWorkers; // 获取正在运行的作业队列 + + nextPtr = queue; + do { + JobWorkerInfo worker = (JobWorkerInfo)nextPtr; + + if (worker->job_id == jobid) { + /* 作业正在执行 */ + LWLockRelease(JobShmemLock); + return JOB_WORKER_RUNNING; + } + nextPtr = nextPtr->next; + } while (nextPtr != queue); + + if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker && + t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker->job_id == jobid) { + /* 作业准备执行 */ + LWLockRelease(JobShmemLock); + return JOB_WORKER_STARTING; + } + + /* 作业未激活 */ + LWLockRelease(JobShmemLock); + return JOB_WORKER_INACTIVE; +} + +static inline bool IsExecuteOnCurrentNode(const char* executeNodeName) +{ + // 检查作业是否在当前节点执行 + + // 检查执行节点名称是否与当前节点名称相同 + if (strcmp(executeNodeName, g_instance.attr.attr_common.PGXCNodeName) == 0) + return true; + + // 检查执行节点名称是否为特殊值"ALL" + if (strcmp(executeNodeName, PGJOB_TYPE_ALL) == 0) + return true; + + // 如果当前节点是协调器 + if (IS_PGXC_COORDINATOR) { + // 检查执行节点名称是否为特殊值"ALL_CN" + if (strcmp(executeNodeName, PGJOB_TYPE_ALL_CN) == 0) { + return true; + } + // 检查执行节点名称是否为特殊值"CCN",并且当前节点是中心节点 + else if (strcmp(executeNodeName, PGJOB_TYPE_CCN) == 0) { + return is_pgxc_central_nodename(g_instance.attr.attr_common.PGXCNodeName); + } else { + return false; + } + } + + // 如果当前节点是数据节点,并且执行节点名称为特殊值"ALL_DN" + if (IS_PGXC_DATANODE && strcmp(executeNodeName, PGJOB_TYPE_ALL_DN) == 0) + return true; + + // 默认情况下,作业不在当前节点执行 + return false; +} + +/* + * @brief SkipSchedulerJob + * Skip process DBE_SCHEDULER jobs, contains various checks. + * 1. whether the job is enabled + * 2. whether the job is expired(end_date < current timestamp OR end_date is NULL) + * @param values pg_job attr values + * @param nulls pg_job attr nulls + * @return true skip current job + * @return false do not skip current job + */ +static bool SkipSchedulerJob(Datum *values, bool *nulls, Timestamp curtime) +{ + Assert(values != NULL); + Assert(nulls != NULL); + + // 不处理非调度作业 + if (nulls[Anum_pg_job_job_name]) { + return false; + } + + // 过期的作业,即使已禁用也需要删除 + if (DatumGetBool(DirectFunctionCall2(timestamp_ge, curtime, values[Anum_pg_job_end_date - 1]))) { + return false; + } + + // 禁用的作业 + if (!nulls[Anum_pg_job_enable - 1] && !DatumGetBool(values[Anum_pg_job_enable - 1])) { + return true; // 在此跳过以避免进一步开销 + } + + return false; +} +/* + * Description: Find expire jobs and insert to job queue for execute. + * + * Returns: void + */ + +static void ScanExpireJobs() +{ + Relation pg_job_tbl = NULL; // 声明关系变量 pg_job_tbl,初始化为 NULL + TableScanDesc scan = NULL; // 声明表扫描描述符 scan,初始化为 NULL + HeapTuple tuple = NULL; // 声明堆元组变量 tuple,初始化为 NULL + MemoryContext oldCtx = NULL; // 声明内存上下文变量 oldCtx,初始化为 NULL + int jobStatus = JOB_WORKER_INACTIVE; // 声明作业状态变量 jobStatus,初始化为 JOB_WORKER_INACTIVE + Datum curtime = DirectFunctionCall1(timestamptz_timestamp, GetCurrentTimestamp()); // 获取当前时间并转换为 Datum 类型 + + StartTransactionCommand(); // 开始事务 + + pg_job_tbl = heap_open(PgJobRelationId, AccessShareLock); // 打开表 PgJobRelationId 并获取关系对象 pg_job_tbl + scan = tableam_scan_begin(pg_job_tbl, SnapshotNow, 0, NULL); // 开始对关系进行表扫描,返回扫描描述符 scan + + MemoryContextReset(t_thrd.job_cxt.ExpiredJobListCtx); // 重置内存上下文 ExpiredJobListCtx + oldCtx = MemoryContextSwitchTo(t_thrd.job_cxt.ExpiredJobListCtx); // 切换到内存上下文 ExpiredJobListCtx + /* 如果已过期作业列表为空,则构建一个新的作业列表。 */ + t_thrd.job_cxt.ExpiredJobList = DLNewList(); // 创建一个新的双向链表作为已过期作业列表 + while (HeapTupleIsValid(tuple = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection))) { + Form_pg_job pg_job = (Form_pg_job)GETSTRUCT(tuple); // 获取堆元组中的作业结构体 + Datum values[Natts_pg_job]; // 声明 values 数组,用于存储作业属性值 + bool nulls[Natts_pg_job]; // 声明 nulls 数组,用于存储作业属性是否为空的标志位 + char status = pg_job->job_status; // 获取作业状态 + int64 jobID = pg_job->job_id; // 获取作业ID + + get_job_values(jobID, tuple, pg_job_tbl, values, nulls); // 获取作业的属性值和空值标志位 + + /* 如果是跳过的调度作业,则继续下一次循环 */ + if (SkipSchedulerJob(values, nulls, curtime)) { + continue; + } + + /* 处理 ALL_NODE/ALL_CN/ALL_DN/CCN 特定节点的情况 */ + if (!IsExecuteOnCurrentNode(pg_job->node_name.data)) { + continue; + } + + /* 如果当前时间小于下次运行时间,则跳过 */ + if (false == DatumGetBool(DirectFunctionCall2(timestamp_gt, curtime, values[Anum_pg_job_next_run_date - 1]))) { + continue; + } + + jobStatus = GetJobStatus(jobID); // 获取作业的状态 + if (PGJOB_RUN_STATUS == status) { // 如果作业状态为运行状态 + if (JOB_WORKER_INACTIVE != jobStatus) { // 如果作业不是非活跃状态,则跳过 + continue; + } + + /* 准备执行作业,因为它不在执行中 */ + } else if (PGJOB_ABORT_STATUS == status) { // 如果作业状态为中止状态,则跳过 + continue; + } else { + /* 准备执行作业 */ + Assert(PGJOB_FAIL_STATUS == pg_job->job_status || PGJOB_SUCC_STATUS == pg_job->job_status); + + if (JOB_WORKER_RUNNING == jobStatus) { // 如果作业状态为运行中 + /* + * 1. 跳过长时间作业检查,因为作业即将完成 + * 2. 跳过作业执行,因为作业正在运行 + */ + continue; + } else if (JOB_WORKER_STARTING == jobStatus) { // 如果作业状态为启动中 + ereport(WARNING, (errmsg("[job id %ld] worker is in risk of startup timeout", jobID))); + /* 跳过,因为作业工作者正在启动 */ + continue; + } else { + Assert(JOB_WORKER_INACTIVE == jobStatus); + /* 准备执行作业,因为它不在执行中 */ + } + } + + Oid dboid = get_database_oid(NameStr(pg_job->dbname), true); // 获取作业所属数据库的 OID + if (!OidIsValid(dboid)) { + /* 跳过,因为作业所属的数据库不存在 */ + ereport(LOG, (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%s\" of job %ld does not exist", NameStr(pg_job->dbname), jobID))); + continue; + } + + // 创建 JobInfo 对象并进行初始化 + JobInfo jobInfo = (JobInfoData*)palloc0(sizeof(JobInfoData)); + jobInfo->job_id = jobID; + jobInfo->job_oid = HeapTupleGetOid(tuple); + jobInfo->job_dboid = dboid; + jobInfo->log_user = pg_job->log_user; + jobInfo->node_name = pg_job->node_name; + jobInfo->last_start_date = + (nulls[Anum_pg_job_last_start_date - 1] ? 0 : values[Anum_pg_job_last_start_date - 1]); + + // 将 JobInfo 对象按照顺序插入已过期作业列表中 + DLInsertByOrder(t_thrd.job_cxt.ExpiredJobList, DLNewElem(jobInfo), JobComparator); + } + + (void)MemoryContextSwitchTo(oldCtx); // 切换回旧的内存上下文 + tableam_scan_end(scan); // 结束表扫描 + heap_close(pg_job_tbl, AccessShareLock); // 关闭关系对象 pg_job_tbl + + CommitTransactionCommand(); // 提交事务 +} + + +/* + * Description: Compare with last_start_date and decide the smaller will insert previes. + * + * Parameters: + * @in baseOne: base job + * @in newOne: new job + * Returns: int + */ +static int JobComparator(const void* baseOne, const void* newOne) +{ + // 比较两个 JobInfo 对象的 last_start_date 属性值 + if (((const JobInfo)newOne)->last_start_date <= ((const JobInfo)baseOne)->last_start_date) { + return -1; // 如果 newOne 的 last_start_date 小于等于 baseOne 的 last_start_date,返回 -1 + } else { + return 1; // 如果 newOne 的 last_start_date 大于 baseOne 的 last_start_date,返回 1 + } +} + + +/* + * Description: Send SIGUSR2 to postmaster and start a new job worker. + * + * Returns: void + */ + //激活作业工作者 +static void ActivateWorker() +{ + JobWorkerInfo worker = NULL; + JobInfo jobInfo = NULL; + DlelemPtr head_job = NULL; + + // 如果已过期作业列表为空,则立即返回,主循环会在一段时间后再次获取作业 + if (DLIsEmpty(t_thrd.job_cxt.ExpiredJobList)) { + return; + } + + // 当没有空闲的作业工作者时,快速返回 + LWLockAcquire(JobShmemLock, LW_SHARED); + worker = t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers; + if (NULL == worker) { + LWLockRelease(JobShmemLock); + ereport(LOG, (errmsg("skip to activate job since no free job workers."))); + return; + } + LWLockRelease(JobShmemLock); + + // 从过期作业列表中移除并返回首个作业 + head_job = DLRemHead(t_thrd.job_cxt.ExpiredJobList); + if (NULL == head_job || NULL == head_job->dle_val) { + /* + * 如果过期作业列表无效,抛出错误,调度器的执行环境将在 JobScheduleMain 函数中重置 + */ + ereport(ERROR, + ((errcode(ERRCODE_INVALID_STATUS), + errmsg("refuse to activate a job since illegal element in ExpiredJobList.")))); + } + jobInfo = (JobInfo)(head_job->dle_val); + + LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); + + // 从空闲作业工作者列表中获取一个工作者,并启动它 + worker = t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers; + if (NULL == worker) { + LWLockRelease(JobShmemLock); + // 记录错误日志,并退出进程 + ereport(FATAL, (errmsg("no free slot when start job worker"))); + return; + } + + t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = (JobWorkerInfo)worker->job_links.next; + + // 设置工作者的相关属性 + worker->job_dboid = jobInfo->job_dboid; + worker->job_id = jobInfo->job_id; + worker->job_oid = jobInfo->job_oid; + worker->username = jobInfo->log_user; + worker->job_launchtime = GetCurrentTimestamp(); + worker->job_worker_pid = UNKNOW_PID; + + t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = worker; + + LWLockRelease(JobShmemLock); + + // 通知 postmaster 启动一个新的作业工作者 + SendPostmasterSignal(PMSIGNAL_START_JOB_WORKER); + DLFreeElem(head_job); + + elog(LOG, "Job scheduler send signal to postmaster to start job worker, jobid=%d.", worker->job_id); +} + + +/* + * Description: Check job's status is 'r' and update to 'f' when start job scheduler thread. + * + * Returns: void + */ +/* + * 检查作业信息 + */ +static void check_jobinfo() +{ + ResourceOwner save = t_thrd.utils_cxt.CurrentResourceOwner; + + // 开启一个事务 + StartTransactionCommand(); + (void)GetTransactionSnapshot(); + + PG_TRY(); + { + // 检查状态为 'r' 的作业,并将其状态更新为 'f' + update_run_job_to_fail(); + CommitTransactionCommand(); + } + PG_CATCH(); + { + // 处理异常并记录错误日志 + FlushErrorState(); + elog(LOG, "Check job info failed"); + AbortCurrentTransaction(); + } + PG_END_TRY(); + + // 恢复当前资源拥有者 + t_thrd.utils_cxt.CurrentResourceOwner = save; +} + + +/* + * Description: Shared memory size. + * + * Returns: Size + */ +/* + * 计算作业信息共享内存大小 + */ +Size JobInfoShmemSize(void) +{ + Size size; + + /* 需要固定结构体和 JobWorkerInfoData 数组的内存空间 */ + + // 计算 JobScheduleShmemStruct 结构体的大小,并按需对齐 + size = sizeof(JobScheduleShmemStruct); + size = MAXALIGN(size); + + // 计算 JobWorkerInfoData 数组的大小,并添加到总大小中 + size = add_size(size, mul_size(g_instance.attr.attr_sql.job_queue_processes, sizeof(JobWorkerInfoData))); + + return size; +} + + +/* + * Description: Init shared memory. + * + * Returns: void + */ +/* + * 初始化作业信息共享内存 + */ +void JobInfoShmemInit(void) +{ + bool found = false; + + // 通过 ShmemInitStruct 函数获取共享内存指针 + t_thrd.job_cxt.JobScheduleShmem = + (JobScheduleShmemStruct*)ShmemInitStruct("Job Scheduler Data", JobInfoShmemSize(), &found); + + if (!IsUnderPostmaster) { + // 如果是在 Postmaster 进程中,则初始化共享内存结构体 + + // 确保共享内存尚未分配 + AssertEreport(!found, MOD_EXECUTOR, ""); + + // 初始化 JobScheduleShmemStruct 结构体 + t_thrd.job_cxt.JobScheduleShmem->jsch_pid = 0; + t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = NULL; + SHMQueueInit(&t_thrd.job_cxt.JobScheduleShmem->jsch_runningWorkers); + t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = NULL; + + // 初始化 JobWorkerInfoData 数组 + JobWorkerInfo worker = (JobWorkerInfo)((char*)t_thrd.job_cxt.JobScheduleShmem + MAXALIGN(sizeof(JobScheduleShmemStruct))); + for (int i = 0; i < g_instance.attr.attr_sql.job_queue_processes; ++i) { + worker[i].job_links.next = (SHM_QUEUE*)(t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers); + t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = &worker[i]; + } + } else { + // 如果不是在 Postmaster 进程中,则确保共享内存已分配 + AssertEreport(found, MOD_EXECUTOR, ""); + } +} + + +/* + * Description: return true if the thread is job scheduler. + * + * Returns: bool + */ +/* + * 判断当前进程是否为作业调度器进程 + */ +bool IsJobSchedulerProcess(void) +{ + // 判断当前进程的角色是否为 JOB_SCHEDULER + return t_thrd.role == JOB_SCHEDULER; +} + + +/* + * RecordForkJobWorkerFailed: Called from postmaster when a worker could not be forked. + * + * Returns: void + */ +/* + * 记录 fork 子进程作业工作者失败状态 + */ +void RecordForkJobWorkerFailed(void) +{ + // 将 ForkJobWorkerFailed 信号置为 true + t_thrd.job_cxt.JobScheduleShmem->jsch_signal[ForkJobWorkerFailed] = true; +} + -- 2.34.1 From 6ce1316fcb9095a79f73ddc34e2f4d95b83d74fa Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:16:50 +0800 Subject: [PATCH 51/56] Delete 'src/gausskernel/process/job/job_worker.cpp' --- src/gausskernel/process/job/job_worker.cpp | 356 --------------------- 1 file changed, 356 deletions(-) delete mode 100755 src/gausskernel/process/job/job_worker.cpp diff --git a/src/gausskernel/process/job/job_worker.cpp b/src/gausskernel/process/job/job_worker.cpp deleted file mode 100755 index 28330be1f..000000000 --- a/src/gausskernel/process/job/job_worker.cpp +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * job_worker.cpp - * Function for start JobWorker thread, and execute current job. - * - * IDENTIFICATION - * src/gausskernel/process/job/job_worker.cpp - * - * ------------------------------------------------------------------------- - */ -#include "postgres.h" -#include "knl/knl_variable.h" - -#include -#ifndef WIN32 -#include -#endif -#include "lib/dllist.h" -#include "access/heapam.h" -#include "access/reloptions.h" -#include "access/transam.h" -#include "access/xact.h" -#include "catalog/dependency.h" -#include "catalog/namespace.h" -#include "catalog/pg_database.h" -#include "commands/dbcommands.h" -#include "commands/vacuum.h" -#include "distributelayer/streamMain.h" -#include "gssignal/gs_signal.h" -#include "libpq/libpq.h" -#include "libpq/pqsignal.h" -#include "miscadmin.h" -#include "pgstat.h" -#include "pgxc/pgxcnode.h" -#include "postmaster/autovacuum.h" -#include "postmaster/fork_process.h" -#include "postmaster/postmaster.h" -#include "storage/buf/bufmgr.h" -#include "storage/ipc.h" -#include "storage/latch.h" -#include "storage/pmsignal.h" -#include "storage/proc.h" -#include "storage/procsignal.h" -#include "storage/sinvaladt.h" -#include "tcop/tcopprot.h" -#include "utils/fmgroids.h" -#include "utils/globalplancore.h" -#include "utils/lsyscache.h" -#include "utils/memutils.h" -#include "utils/postinit.h" -#include "utils/ps_status.h" -#include "utils/rel.h" -#include "utils/rel_gs.h" -#include "utils/snapmgr.h" -#include "utils/syscache.h" -#include "utils/timestamp.h" -#include "access/heapam.h" -#include "utils/builtins.h" -#include "catalog/pg_job.h" -#include "catalog/pg_job_proc.h" -#include "job/job_shmem.h" -#include "job/job_worker.h" - -/***************************************************************************** - * PRIVATE FIELD DEFINE - ****************************************************************************/ -#define UNKNOW_PID ((ThreadId)(-1)) - -/***************************************************************************** - * PRIVATE FUNCTION DEFINE - ****************************************************************************/ -static void SetupSignalHook(void); -static void FreeJobWorkerInfo(int code, Datum arg); - -/***************************************************************************** - * JOB WORKER IMPLEMENTS CODE : PRIVATE - ****************************************************************************/ -/* - * Description: Return true if the thread is job worker. - * - * Returns: bool - */ -bool IsJobWorkerProcess(void) -{ - return t_thrd.role == JOB_WORKER; -} - -/* - * Description: Register signal process for job worker. - * - * Returns: void - */ -static void SetupSignalHook(void) -{ - (void)gspqsignal(SIGHUP, SIG_IGN); - (void)gspqsignal(SIGQUIT, quickdie); - (void)gspqsignal(SIGTERM, die); - (void)gspqsignal(SIGINT, StatementCancelHandler); /* cancel current query */ - (void)gspqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */ - (void)gspqsignal(SIGPIPE, SIG_IGN); - (void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); - (void)gspqsignal(SIGUSR2, SIG_IGN); - (void)gspqsignal(SIGFPE, FloatExceptionHandler); - (void)gspqsignal(SIGCHLD, SIG_DFL); -} - -/* - * Description: Free job worker info when thread exit. - * - * Returns: void - */ -static void FreeJobWorkerInfo(int code, Datum arg) -{ - if (t_thrd.job_cxt.MyWorkerInfo != NULL) { - (void)LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); - - SHMQueueDelete(&t_thrd.job_cxt.MyWorkerInfo->job_links); - t_thrd.job_cxt.MyWorkerInfo->job_links.next = (SHM_QUEUE*)t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers; - t_thrd.job_cxt.MyWorkerInfo->job_dboid = InvalidOid; - t_thrd.job_cxt.MyWorkerInfo->job_id = InvalidOid; - t_thrd.job_cxt.MyWorkerInfo->job_launchtime = 0; - t_thrd.job_cxt.MyWorkerInfo->job_worker_pid = UNKNOW_PID; - t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = t_thrd.job_cxt.MyWorkerInfo; - t_thrd.job_cxt.MyWorkerInfo = NULL; - - LWLockRelease(JobShmemLock); - } -} - -/* - * Description: Main loop for the job worker process. - * - * Parameters: - * @in argc: the number of args. - * @in argv: detail info for each args. - * Returns: void - */ -void JobExecuteWorkerMain() -{ - sigjmp_buf local_sigjmp_buf; - Oid dboid = InvalidOid; - int4 job_id = -1; - char* username = NULL; - MemoryContext oldcontext = NULL; - - /* we are a postmaster subprocess now */ - IsUnderPostmaster = true; - t_thrd.role = JOB_WORKER; - - /* reset t_thrd.proc_cxt.MyProcPid */ - t_thrd.proc_cxt.MyProcPid = gs_thread_self(); - - t_thrd.proc_cxt.MyProgName = "JobExecuteWorker"; - - /* record Start Time for logging */ - t_thrd.proc_cxt.MyStartTime = time(NULL); - - /* Identify myself via ps */ - init_ps_display("Job worker process", "", "", ""); - - /* set processing mode */ - SetProcessingMode(InitProcessing); - - /* setup signal process hook */ - SetupSignalHook(); - - gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); - (void)gs_signal_unblock_sigusr2(); - - /* Early initialization */ - BaseInit(); - -#ifndef EXEC_BACKEND - InitProcess(); -#endif - - /* - * Create the memory context we will use in the main loop. - * - * t_thrd.mem_cxt.msg_mem_cxt is reset once per iteration of the main loop, ie, upon - * completion of processing of each command message from the client. - */ - t_thrd.mem_cxt.msg_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, - "MessageContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - t_thrd.mem_cxt.mask_password_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, - "MaskPasswordCtx", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - InitVecFuncMap(); - - (void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); - - /* If an exception is encountered, processing resumes here. */ - int curTryCounter; - int* oldTryCounter = NULL; - if (sigsetjmp(local_sigjmp_buf, 1) != 0) { - gstrace_tryblock_exit(true, oldTryCounter); - - /* Prevents interrupts while cleaning up */ - HOLD_INTERRUPTS(); - - /* Report the error to the server log */ - EmitErrorReport(); - - if (job_id > 0) { - ereport(LOG, (errmsg("job worker with job id %d shutdown abnormaly", job_id))); - } - - (void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); - FlushErrorState(); - - /* Flush any leaked data in the top-level context */ - MemoryContextResetAndDeleteChildren(t_thrd.mem_cxt.msg_mem_cxt); - - /* release resource held by lsc */ - AtEOXact_SysDBCache(false); - - LWLockReleaseAll(); - if (t_thrd.utils_cxt.CurrentResourceOwner) { - ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); - } - - /* - * process exit. Note that because we called InitProcess, a - * callback was registered to do ProcKill, which will clean up - * necessary state. - */ - proc_exit(0); - } - oldTryCounter = gstrace_tryblock_entry(&curTryCounter); - - /* We can now handle ereport(ERROR) */ - t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; - - /* We need to allow SIGINT, etc during the initial transaction */ - gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); - (void)gs_signal_unblock_sigusr2(); - - /* Get job info from shared memory */ - LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); - if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker != NULL) { - t_thrd.job_cxt.MyWorkerInfo = t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker; - t_thrd.job_cxt.MyWorkerInfo->job_worker_pid = t_thrd.proc_cxt.MyProcPid; - dboid = t_thrd.job_cxt.MyWorkerInfo->job_dboid; - job_id = t_thrd.job_cxt.MyWorkerInfo->job_id; - - username = pstrdup(NameStr(t_thrd.job_cxt.MyWorkerInfo->username)); - - SHMQueueInsertBefore( - &t_thrd.job_cxt.JobScheduleShmem->jsch_runningWorkers, &t_thrd.job_cxt.MyWorkerInfo->job_links); - - /* - * Remove from the "starting" pointer, so that the launcher can start - * a new worker if required - */ - t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = NULL; - - LWLockRelease(JobShmemLock); - - /* setup shared memory hook */ - on_shmem_exit(FreeJobWorkerInfo, 0); - on_shmem_exit(PGXCNodeCleanAndRelease, 0); - ereport(LOG, (errmsg("job worker started with job id: %d", job_id))); - } else { - LWLockRelease(JobShmemLock); - - /* no worker entry for me, go away */ - ereport(WARNING, (errmsg("job worker started wihtout worker entry"))); - proc_exit(0); - } - - /* user_name and database_name in u_sess->proc_cxt.MyProcPort is under t_thrd.top_mem_cxt */ - oldcontext = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); - if (u_sess->proc_cxt.MyProcPort->database_name) - pfree_ext(u_sess->proc_cxt.MyProcPort->database_name); - if (u_sess->proc_cxt.MyProcPort->user_name) - pfree_ext(u_sess->proc_cxt.MyProcPort->user_name); - u_sess->proc_cxt.MyProcPort->database_name = (char*)palloc0(NAMEDATALEN); - u_sess->proc_cxt.MyProcPort->user_name = pstrdup(username); - (void)MemoryContextSwitchTo(oldcontext); - - u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp(); - - /* General initialization. */ - t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(NULL, dboid, username); - t_thrd.proc_cxt.PostInit->InitJobExecuteWorker(); - t_thrd.proc_cxt.PostInit->GetDatabaseName(u_sess->proc_cxt.MyProcPort->database_name); - -#ifdef PGXC /* PGXC_COORD */ - /* - * Initialize key pair to be used as object id while using advisory lock - * for backup - */ - t_thrd.postmaster_cxt.xc_lockForBackupKey1 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_1); - t_thrd.postmaster_cxt.xc_lockForBackupKey2 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_2); -#endif - - /* report this backend in the PgBackendStatus array */ - pgstat_report_appname("JobWorker"); - pgstat_report_activity(STATE_IDLE, NULL); - pgstat_report_jobid(job_id); /* Record job id into beentry */ - - /* It should enter running state for ExecRemoteUtility. */ - pgstat_report_activity(STATE_RUNNING, NULL); - - /* Reset some flag related to stream. */ - ResetStreamEnv(); - - t_thrd.role = JOB_WORKER; - - t_thrd.wlm_cxt.thread_node_group = &g_instance.wlm_cxt->MyDefaultNodeGroup; // initialize the default value - t_thrd.wlm_cxt.thread_climgr = &t_thrd.wlm_cxt.thread_node_group->climgr; - t_thrd.wlm_cxt.thread_srvmgr = &t_thrd.wlm_cxt.thread_node_group->srvmgr; - - /* - * Create a resource owner to keep track of our resources (currently only - * buffer pins). - */ - t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Job Worker", - THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); - - /* Get classified list of node Oids for syschronise th job info. */ - exec_init_poolhandles(); - (void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); - - SetProcessingMode(NormalProcessing); - - /* execute job procedure */ - elog(LOG, "Job is running, worker: %lu, job id: %d", t_thrd.proc_cxt.MyProcPid, job_id); - execute_job(job_id); - elog(LOG, "Job worker is shutdown normal."); - - MemoryContextResetAndDeleteChildren(t_thrd.mem_cxt.msg_mem_cxt); - - /* All done, go away */ - proc_exit(0); -} -- 2.34.1 From 47d827797bf07841fccfd5be8ebb950cd7621d45 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:17:06 +0800 Subject: [PATCH 52/56] ADD file via upload --- src/gausskernel/process/job/job_worker.cpp | 373 +++++++++++++++++++++ 1 file changed, 373 insertions(+) create mode 100644 src/gausskernel/process/job/job_worker.cpp diff --git a/src/gausskernel/process/job/job_worker.cpp b/src/gausskernel/process/job/job_worker.cpp new file mode 100644 index 000000000..0853f0b7a --- /dev/null +++ b/src/gausskernel/process/job/job_worker.cpp @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * 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. + * ------------------------------------------------------------------------- + * + * job_worker.cpp + * Function for start JobWorker thread, and execute current job. + * + * IDENTIFICATION + * src/gausskernel/process/job/job_worker.cpp + * + * ------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "knl/knl_variable.h" + +#include +#ifndef WIN32 +#include +#endif +#include "lib/dllist.h" +#include "access/heapam.h" +#include "access/reloptions.h" +#include "access/transam.h" +#include "access/xact.h" +#include "catalog/dependency.h" +#include "catalog/namespace.h" +#include "catalog/pg_database.h" +#include "commands/dbcommands.h" +#include "commands/vacuum.h" +#include "distributelayer/streamMain.h" +#include "gssignal/gs_signal.h" +#include "libpq/libpq.h" +#include "libpq/pqsignal.h" +#include "miscadmin.h" +#include "pgstat.h" +#include "pgxc/pgxcnode.h" +#include "postmaster/autovacuum.h" +#include "postmaster/fork_process.h" +#include "postmaster/postmaster.h" +#include "storage/buf/bufmgr.h" +#include "storage/ipc.h" +#include "storage/latch.h" +#include "storage/pmsignal.h" +#include "storage/proc.h" +#include "storage/procsignal.h" +#include "storage/sinvaladt.h" +#include "tcop/tcopprot.h" +#include "utils/fmgroids.h" +#include "utils/globalplancore.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/postinit.h" +#include "utils/ps_status.h" +#include "utils/rel.h" +#include "utils/rel_gs.h" +#include "utils/snapmgr.h" +#include "utils/syscache.h" +#include "utils/timestamp.h" +#include "access/heapam.h" +#include "utils/builtins.h" +#include "catalog/pg_job.h" +#include "catalog/pg_job_proc.h" +#include "job/job_shmem.h" +#include "job/job_worker.h" + +/***************************************************************************** + * PRIVATE FIELD DEFINE + ****************************************************************************/ +#define UNKNOW_PID ((ThreadId)(-1)) + +/***************************************************************************** + * PRIVATE FUNCTION DEFINE + ****************************************************************************/ +static void SetupSignalHook(void); +static void FreeJobWorkerInfo(int code, Datum arg); + +/***************************************************************************** + * JOB WORKER IMPLEMENTS CODE : PRIVATE + ****************************************************************************/ +/* + * Description: Return true if the thread is job worker. + * + * Returns: bool + */ + //此函数用于判断当前线程是否为作业工作进程。如果是作业工作进程,则返回true;否则返回false。 +bool IsJobWorkerProcess(void) +{ + return t_thrd.role == JOB_WORKER; +} + +/* + * Description: Register signal process for job worker. + * + * Returns: void + */ + //此函数用于为作业工作进程注册信号处理程序。 +static void SetupSignalHook(void) +{ + (void)gspqsignal(SIGHUP, SIG_IGN); + (void)gspqsignal(SIGQUIT, quickdie); + (void)gspqsignal(SIGTERM, die); + (void)gspqsignal(SIGINT, StatementCancelHandler); /* cancel current query */ + (void)gspqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */ + (void)gspqsignal(SIGPIPE, SIG_IGN); + (void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); + (void)gspqsignal(SIGUSR2, SIG_IGN); + (void)gspqsignal(SIGFPE, FloatExceptionHandler); + (void)gspqsignal(SIGCHLD, SIG_DFL); +} + +/* + * Description: Free job worker info when thread exit. + * + * Returns: void + */ + //此函数用于在线程退出时释放作业工作进程的信息。 +static void FreeJobWorkerInfo(int code, Datum arg) +{ + if (t_thrd.job_cxt.MyWorkerInfo != NULL) { + (void)LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); + + SHMQueueDelete(&t_thrd.job_cxt.MyWorkerInfo->job_links); + t_thrd.job_cxt.MyWorkerInfo->job_links.next = (SHM_QUEUE*)t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers; + t_thrd.job_cxt.MyWorkerInfo->job_dboid = InvalidOid; + t_thrd.job_cxt.MyWorkerInfo->job_id = InvalidOid; + t_thrd.job_cxt.MyWorkerInfo->job_launchtime = 0; + t_thrd.job_cxt.MyWorkerInfo->job_worker_pid = UNKNOW_PID; + t_thrd.job_cxt.JobScheduleShmem->jsch_freeWorkers = t_thrd.job_cxt.MyWorkerInfo; + t_thrd.job_cxt.MyWorkerInfo = NULL; + + LWLockRelease(JobShmemLock); + } +} + +/* + * Description: Main loop for the job worker process. + * + * Parameters: + * @in argc: the number of args. + * @in argv: detail info for each args. + * Returns: void + */ + /* + 这个函数是一个作业执行器的主函数。它实现了以下功能: + +初始化进程和线程上下文。 +创建内存上下文并设置处理模式。 +设置信号处理钩子和信号屏蔽。 +处理异常和错误,记录日志并清理资源。 +从共享内存获取作业信息,并将自己添加到运行中的作业工作者列表中。 +设置会话用户名和数据库名。 +执行作业初始化操作。 +报告自己的状态和活动到PgBackendStatus和pg_stat_activity。 +执行作业具体的逻辑。 +清理资源并退出进程。 +*/ +void JobExecuteWorkerMain() +{ + sigjmp_buf local_sigjmp_buf; + Oid dboid = InvalidOid; + int4 job_id = -1; + char* username = NULL; + MemoryContext oldcontext = NULL; + + /* we are a postmaster subprocess now */ + IsUnderPostmaster = true; + t_thrd.role = JOB_WORKER; + + /* reset t_thrd.proc_cxt.MyProcPid */ + t_thrd.proc_cxt.MyProcPid = gs_thread_self(); + + t_thrd.proc_cxt.MyProgName = "JobExecuteWorker"; + + /* record Start Time for logging */ + t_thrd.proc_cxt.MyStartTime = time(NULL); + + /* Identify myself via ps */ + init_ps_display("Job worker process", "", "", ""); + + /* set processing mode */ + SetProcessingMode(InitProcessing); + + /* setup signal process hook */ + SetupSignalHook(); + + gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); + (void)gs_signal_unblock_sigusr2(); + + /* Early initialization */ + BaseInit(); + +#ifndef EXEC_BACKEND + InitProcess(); +#endif + + /* + * Create the memory context we will use in the main loop. + * + * t_thrd.mem_cxt.msg_mem_cxt is reset once per iteration of the main loop, ie, upon + * completion of processing of each command message from the client. + */ + t_thrd.mem_cxt.msg_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, + "MessageContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + t_thrd.mem_cxt.mask_password_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, + "MaskPasswordCtx", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + InitVecFuncMap(); + + (void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); + + /* If an exception is encountered, processing resumes here. */ + int curTryCounter; + int* oldTryCounter = NULL; + if (sigsetjmp(local_sigjmp_buf, 1) != 0) { + gstrace_tryblock_exit(true, oldTryCounter); + + /* Prevents interrupts while cleaning up */ + HOLD_INTERRUPTS(); + + /* Report the error to the server log */ + EmitErrorReport(); + + if (job_id > 0) { + ereport(LOG, (errmsg("job worker with job id %d shutdown abnormaly", job_id))); + } + + (void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); + FlushErrorState(); + + /* Flush any leaked data in the top-level context */ + MemoryContextResetAndDeleteChildren(t_thrd.mem_cxt.msg_mem_cxt); + + /* release resource held by lsc */ + AtEOXact_SysDBCache(false); + + LWLockReleaseAll(); + if (t_thrd.utils_cxt.CurrentResourceOwner) { + ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); + } + + /* + * process exit. Note that because we called InitProcess, a + * callback was registered to do ProcKill, which will clean up + * necessary state. + */ + proc_exit(0); + } + oldTryCounter = gstrace_tryblock_entry(&curTryCounter); + + /* We can now handle ereport(ERROR) */ + t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; + + /* We need to allow SIGINT, etc during the initial transaction */ + gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); + (void)gs_signal_unblock_sigusr2(); + + /* Get job info from shared memory */ + LWLockAcquire(JobShmemLock, LW_EXCLUSIVE); + if (t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker != NULL) { + t_thrd.job_cxt.MyWorkerInfo = t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker; + t_thrd.job_cxt.MyWorkerInfo->job_worker_pid = t_thrd.proc_cxt.MyProcPid; + dboid = t_thrd.job_cxt.MyWorkerInfo->job_dboid; + job_id = t_thrd.job_cxt.MyWorkerInfo->job_id; + + username = pstrdup(NameStr(t_thrd.job_cxt.MyWorkerInfo->username)); + + SHMQueueInsertBefore( + &t_thrd.job_cxt.JobScheduleShmem->jsch_runningWorkers, &t_thrd.job_cxt.MyWorkerInfo->job_links); + + /* + * Remove from the "starting" pointer, so that the launcher can start + * a new worker if required + */ + t_thrd.job_cxt.JobScheduleShmem->jsch_startingWorker = NULL; + + LWLockRelease(JobShmemLock); + + /* setup shared memory hook */ + on_shmem_exit(FreeJobWorkerInfo, 0); + on_shmem_exit(PGXCNodeCleanAndRelease, 0); + ereport(LOG, (errmsg("job worker started with job id: %d", job_id))); + } else { + LWLockRelease(JobShmemLock); + + /* no worker entry for me, go away */ + ereport(WARNING, (errmsg("job worker started wihtout worker entry"))); + proc_exit(0); + } + + /* user_name and database_name in u_sess->proc_cxt.MyProcPort is under t_thrd.top_mem_cxt */ + oldcontext = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); + if (u_sess->proc_cxt.MyProcPort->database_name) + pfree_ext(u_sess->proc_cxt.MyProcPort->database_name); + if (u_sess->proc_cxt.MyProcPort->user_name) + pfree_ext(u_sess->proc_cxt.MyProcPort->user_name); + u_sess->proc_cxt.MyProcPort->database_name = (char*)palloc0(NAMEDATALEN); + u_sess->proc_cxt.MyProcPort->user_name = pstrdup(username); + (void)MemoryContextSwitchTo(oldcontext); + + u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp(); + + /* General initialization. */ + t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(NULL, dboid, username); + t_thrd.proc_cxt.PostInit->InitJobExecuteWorker(); + t_thrd.proc_cxt.PostInit->GetDatabaseName(u_sess->proc_cxt.MyProcPort->database_name); + +#ifdef PGXC /* PGXC_COORD */ + /* + * Initialize key pair to be used as object id while using advisory lock + * for backup + */ + t_thrd.postmaster_cxt.xc_lockForBackupKey1 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_1); + t_thrd.postmaster_cxt.xc_lockForBackupKey2 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_2); +#endif + + /* report this backend in the PgBackendStatus array */ + pgstat_report_appname("JobWorker"); + pgstat_report_activity(STATE_IDLE, NULL); + pgstat_report_jobid(job_id); /* Record job id into beentry */ + + /* It should enter running state for ExecRemoteUtility. */ + pgstat_report_activity(STATE_RUNNING, NULL); + + /* Reset some flag related to stream. */ + ResetStreamEnv(); + + t_thrd.role = JOB_WORKER; + + t_thrd.wlm_cxt.thread_node_group = &g_instance.wlm_cxt->MyDefaultNodeGroup; // initialize the default value + t_thrd.wlm_cxt.thread_climgr = &t_thrd.wlm_cxt.thread_node_group->climgr; + t_thrd.wlm_cxt.thread_srvmgr = &t_thrd.wlm_cxt.thread_node_group->srvmgr; + + /* + * Create a resource owner to keep track of our resources (currently only + * buffer pins). + */ + t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Job Worker", + THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); + + /* Get classified list of node Oids for syschronise th job info. */ + exec_init_poolhandles(); + (void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); + + SetProcessingMode(NormalProcessing); + + /* execute job procedure */ + elog(LOG, "Job is running, worker: %lu, job id: %d", t_thrd.proc_cxt.MyProcPid, job_id); + execute_job(job_id); + elog(LOG, "Job worker is shutdown normal."); + + MemoryContextResetAndDeleteChildren(t_thrd.mem_cxt.msg_mem_cxt); + + /* All done, go away */ + proc_exit(0); +} -- 2.34.1 From 92e8042b7b9792c238b48bf5926553e37df6b83e Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:17:25 +0800 Subject: [PATCH 53/56] Delete 'src/gausskernel/process/main/main.cpp' --- src/gausskernel/process/main/main.cpp | 538 -------------------------- 1 file changed, 538 deletions(-) delete mode 100755 src/gausskernel/process/main/main.cpp diff --git a/src/gausskernel/process/main/main.cpp b/src/gausskernel/process/main/main.cpp deleted file mode 100755 index 6a6ca1027..000000000 --- a/src/gausskernel/process/main/main.cpp +++ /dev/null @@ -1,538 +0,0 @@ -/* - * - * main.cpp - * Stub main() routine for the openGauss executable. - * - * This does some essential startup tasks for any incarnation of openGauss - * (postmaster, standalone backend, standalone bootstrap process, or a - * separately exec'd child of a postmaster) and then dispatches to the - * proper FooMain() routine for the incarnation. - * - * - * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/gausskernel/process/main/main.cpp - * - * ------------------------------------------------------------------------- - */ -#include "postgres.h" -#include "knl/knl_variable.h" - -#include -#include - -#if defined(__alpha) && defined(__osf__) /* no __alpha__ ? */ -#include -#include "machine/hal_sysinfo.h" -#define ASSEMBLER -#include -#undef ASSEMBLER -#endif - -#if defined(__NetBSD__) -#include -#endif - -#include "bootstrap/bootstrap.h" -#include "postmaster/postmaster.h" -#include "miscadmin.h" -#include "storage/ipc.h" -#include "tcop/tcopprot.h" -#include "utils/fmgrtab.h" -#include "utils/help_config.h" -#include "utils/pg_locale.h" -#include "utils/ps_status.h" -#ifdef WIN32 -#include "libpq/pqsignal.h" -#endif - -#include "utils/syscall_lock.h" -#include "gssignal/gs_signal.h" -#include "utils/memutils.h" -#include "utils/mmpool.h" -#include "utils/plog.h" -#include "gstrace/gstrace_infra.h" - -THR_LOCAL bool IsInitdb = false; - -size_t mmap_threshold = (size_t)0xffffffff; - -const char* progname = NULL; - -static void startup_hacks(const char* progname); -static void help(const char* progname); -static void check_root(const char* progname); -static char* get_current_username(const char* progname); -static void syscall_lock_init(void); - -extern int encrypte_main(int argc, char* const argv[]); - -/* - * Any openGauss server process begins execution here. - */ -int main(int argc, char* argv[]) -{ - char* mmap_env = NULL; - syscall_lock_init(); - - mmap_env = gs_getenv_r("GAUSS_MMAP_THRESHOLD"); - if (mmap_env != NULL) { - check_backend_env(mmap_env); - mmap_threshold = (size_t)atol(mmap_env); - } - - knl_instance_init(); - - g_instance.increCheckPoint_context = AllocSetContextCreate( - INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), - "IncreCheckPointContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE, - SHARED_CONTEXT); - - g_instance.account_context = AllocSetContextCreate(g_instance.instance_context, - "StandbyAccontContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE, - SHARED_CONTEXT); - - g_instance.comm_cxt.comm_global_mem_cxt = AllocSetContextCreate(g_instance.instance_context, - "CommunnicatorGlobalMemoryContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE, - SHARED_CONTEXT); - - g_instance.builtin_proc_context = AllocSetContextCreate(g_instance.instance_context, - "builtin_procGlobalMemoryContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE, - SHARED_CONTEXT); - /* - * Fire up essential subsystems: error and memory management - * - * Code after this point is allowed to use elog/ereport, though - * localization of messages may not work right away, and messages won't go - * anywhere but stderr until GUC settings get loaded. - */ - MemoryContextInit(); - - PmTopMemoryContext = t_thrd.top_mem_cxt; - - knl_thread_init(MASTER_THREAD); - - t_thrd.fake_session = create_session_context(t_thrd.top_mem_cxt, 0); - t_thrd.fake_session->status = KNL_SESS_FAKE; - - u_sess = t_thrd.fake_session; - - SelfMemoryContext = THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT); - - MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); - - progname = get_progname(argv[0]); - - /* - * Platform-specific startup hacks - */ - startup_hacks(progname); - - /* if gaussdb's name is gs_encrypt, so run in encrypte_main() */ - if (!strcmp(progname, "gs_encrypt")) { - return encrypte_main(argc, argv); - } - - init_plog_global_mem(); - - /* - * Remember the physical location of the initially given argv[] array for - * possible use by ps display. On some platforms, the argv[] storage must - * be overwritten in order to set the process title for ps. In such cases - * save_ps_display_args makes and returns a new copy of the argv[] array. - * - * save_ps_display_args may also move the environment strings to make - * extra room. Therefore this should be done as early as possible during - * startup, to avoid entanglements with code that might save a getenv() - * result pointer. - */ - argv = save_ps_display_args(argc, argv); - - /* - * If supported on the current platform, set up a handler to be called if - * the backend/postmaster crashes with a fatal signal or exception. - */ -#if defined(WIN32) && defined(HAVE_MINIDUMP_TYPE) - pgwin32_install_crashdump_handler(); -#endif - - /* - * Set up locale information from environment. Note that LC_CTYPE and - * LC_COLLATE will be overridden later from pg_control if we are in an - * already-initialized database. We set them here so that they will be - * available to fill pg_control during initdb. LC_MESSAGES will get set - * later during GUC option processing, but we set it here to allow startup - * error messages to be localized. - */ - set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("gaussdb")); - -#ifdef WIN32 - - /* - * Windows uses codepages rather than the environment, so we work around - * that by querying the environment explicitly first for LC_COLLATE and - * LC_CTYPE. We have to do this because initdb passes those values in the - * environment. If there is nothing there we fall back on the codepage. - */ - { - char* env_locale = NULL; - - if ((env_locale = gs_getenv_r("LC_COLLATE")) != NULL) { - check_backend_env(env_locale); - pg_perm_setlocale(LC_COLLATE, env_locale); - } else - pg_perm_setlocale(LC_COLLATE, ""); - - if ((env_locale = gs_getenv_r("LC_CTYPE")) != NULL) { - check_backend_env(env_locale); - pg_perm_setlocale(LC_CTYPE, env_locale); - } else - pg_perm_setlocale(LC_CTYPE, ""); - } -#else - pg_perm_setlocale(LC_COLLATE, ""); - pg_perm_setlocale(LC_CTYPE, ""); -#endif - - /* - * We keep these set to "C" always, except transiently in pg_locale.c; see - * that file for explanations. - */ - pg_perm_setlocale(LC_MONETARY, "C"); - pg_perm_setlocale(LC_NUMERIC, "C"); - pg_perm_setlocale(LC_TIME, "C"); - - /* - * Now that we have absorbed as much as we wish to from the locale - * environment, remove any LC_ALL setting, so that the environment - * variables installed by pg_perm_setlocale have force. - */ - (void)unsetenv("LC_ALL"); - - /* - * Catch standard options before doing much else - */ - if (argc > 1) { - if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { - help(progname); - exit(0); - } - if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { - puts("gaussdb " DEF_GS_VERSION); - exit(0); - } - } - - /* - * Make sure we are not running as root. - */ - check_root(progname); - - /* - * Dispatch to one of various subprograms depending on first argument. - */ -#ifdef WIN32 - - /* - * Start our win32 signal implementation - * - * SubPostmasterMain() will do this for itself, but the remaining modes - * need it here - */ - pgwin32_signal_initialize(); -#endif - - t_thrd.mem_cxt.gs_signal_mem_cxt = AllocSetContextCreate( - t_thrd.top_mem_cxt, "gs_signal", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - if (NULL == t_thrd.mem_cxt.gs_signal_mem_cxt) { - ereport(LOG, (errmsg("could not start a new thread, because of no enough system resource. "))); - proc_exit(1); - } - - /* - * @BuiltinFunc - * Create a global BuiltinFunc object shared among threads - */ - if (g_sorted_funcs[0] == NULL) { - initBuiltinFuncs(); - } - - bool isBoot = (argc > 1 && strcmp(argv[1], "--boot") == 0); - if (isBoot) { - IsInitdb = true; - gs_signal_monitor_startup(); - gs_signal_slots_init(1); - (void)gs_signal_unblock_sigusr2(); - gs_signal_startup_siginfo("AuxiliaryProcessMain"); - BootStrapProcessMain(argc, argv); /* does not return */ - } - - if (argc > 1 && strcmp(argv[1], "--describe-config") == 0) - exit(GucInfoMain()); - - if (argc > 1 && strcmp(argv[1], "--single") == 0) { - IsInitdb = true; - gs_signal_monitor_startup(); - gs_signal_slots_init(1); - (void)gs_signal_unblock_sigusr2(); - gs_signal_startup_siginfo("PostgresMain"); - - exit(PostgresMain(argc, argv, NULL, get_current_username(progname))); - } - - exit(PostmasterMain(argc, argv)); -} - -/* - * Place platform-specific startup hacks here. This is the right - * place to put code that must be executed early in the launch of any new - * server process. Note that this code will NOT be executed when a backend - * or sub-bootstrap process is forked, unless we are in a fork/exec - * environment (ie EXEC_BACKEND is defined). - * - * XXX The need for code here is proof that the platform in question - * is too brain-dead to provide a standard C execution environment - * without help. Avoid adding more here, if you can. - */ -static void startup_hacks(const char* progname) -{ - /* - * On some platforms, unaligned memory accesses result in a kernel trap; - * the default kernel behavior is to emulate the memory access, but this - * results in a significant performance penalty. We want PG never to make - * such unaligned memory accesses, so this code disables the kernel - * emulation: unaligned accesses will result in SIGBUS instead. - */ -#ifdef NOFIXADE - -#if defined(__alpha) /* no __alpha__ ? */ - { - int buffer[] = {SSIN_UACPROC, UAC_SIGBUS | UAC_NOPRINT}; - - if (setsysinfo(SSI_NVPAIRS, buffer, 1, (caddr_t)NULL, (unsigned long)NULL) < 0) - write_stderr("%s: setsysinfo failed: %s\n", progname, gs_strerror(errno)); - } -#endif /* __alpha */ -#endif /* NOFIXADE */ - - /* - * Windows-specific execution environment hacking. - */ -#ifdef WIN32 - { - WSADATA wsaData; - int err; - - /* Make output streams unbuffered by default */ - setvbuf(stdout, NULL, _IONBF, 0); - setvbuf(stderr, NULL, _IONBF, 0); - - /* Prepare Winsock */ - err = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (err != 0) { - write_stderr("%s: WSAStartup failed: %d\n", progname, err); - exit(1); - } - - /* In case of general protection fault, don't show GUI popup box */ - SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); - } -#endif /* WIN32 */ - - /* Binding static TLS variables for current thread */ - EarlyBindingTLSVariables(); -} - -/* - * Help display should match the options accepted by PostmasterMain() - * and PostgresMain(). - */ -static void help(const char* progname) -{ - printf(_("%s is the gaussdb server.\n\n"), progname); - printf(_("Usage:\n %s [OPTION]...\n\n"), progname); - printf(_("Options:\n")); -#ifdef USE_ASSERT_CHECKING - printf(_(" -A 1|0 enable/disable run-time assert checking\n")); -#endif - printf(_(" -B NBUFFERS number of shared buffers\n")); - printf(_(" -b BINARY UPGRADES flag used for binary upgrades\n")); - printf(_(" -c NAME=VALUE set run-time parameter\n")); - printf(_(" -C NAME print value of run-time parameter, then exit\n")); - printf(_(" -d 1-5 debugging level\n")); - printf(_(" -D DATADIR database directory\n")); - printf(_(" -e use European date input format (DMY)\n")); - printf(_(" -F turn fsync off\n")); - printf(_(" -h HOSTNAME host name or IP address to listen on\n")); - printf(_(" -i enable TCP/IP connections\n")); - printf(_(" -k DIRECTORY Unix-domain socket location\n")); -#ifdef USE_SSL - printf(_(" -l enable SSL connections\n")); -#endif - printf(_(" -N MAX-CONNECT maximum number of allowed connections\n")); - printf(_(" -M SERVERMODE the database start as the appointed server mode\n")); - - printf(_(" -o OPTIONS pass \"OPTIONS\" to each server process (obsolete)\n")); - printf(_(" -p PORT port number to listen on\n")); -#ifdef ENABLE_MULTIPLE_NODES - printf(_(" -R indicate run as xlogreiver.Only used with -M standby\n")); -#endif - printf(_(" -s show statistics after each query\n")); - printf(_(" -S WORK-MEM set amount of memory for sorts (in kB)\n")); - printf(_(" -u NUM set the num of kernel version before upgrade\n")); - printf(_(" -V, --version output version information, then exit\n")); - printf(_(" --NAME=VALUE set run-time parameter\n")); - printf(_(" --describe-config describe configuration parameters, then exit\n")); - printf(_(" --securitymode allow database system run in security mode\n")); - printf(_(" --single_node A SingleDN mode is being activated\n")); - printf(_(" -?, --help show this help, then exit\n")); - - printf(_("\nServer mode:\n")); - printf(_(" primary database system starts as a primary server, send xlog to standby server\n")); - printf(_(" standby database system starts as a standby server, receive xlog from primary server\n")); - printf(_(" pending database system starts as a pending server, wait for promoting to primary or " - "demoting to standby\n")); -#ifdef ENABLE_MULTIPLE_NODES - printf(_(" fenced database system starts a fenced master process, serve UDF execution in secure mode " - "(run separately from Gaussdb process)\n")); -#endif - - printf(_("\nDeveloper options:\n")); - printf(_(" -f s|i|n|m|h forbid use of some plan types\n")); - printf(_(" -n do not reinitialize shared memory after abnormal exit\n")); - printf(_(" -O allow system table structure changes\n")); - printf(_(" -P disable system indexes\n")); - printf(_(" -t pa|pl|ex show timings after each query\n")); - printf(_(" -T send SIGSTOP to all backend processes if one dies\n")); - printf(_(" -W NUM wait NUM seconds to allow attach from a debugger\n")); - printf(_(" --localxid use local transaction id (used only by gs_initdb)\n")); - - printf(_("\nOptions for single-user mode:\n")); - printf(_(" --single selects single-user mode (must be first argument)\n")); - printf(_(" DBNAME database name (defaults to user name)\n")); - printf(_(" -d 0-5 override debugging level\n")); - printf(_(" -E echo statement before execution\n")); - printf(_(" -j do not use newline as interactive query delimiter\n")); - printf(_(" -r FILENAME send stdout and stderr to given file\n")); - - printf(_("\nOptions for bootstrapping mode:\n")); - printf(_(" --boot selects bootstrapping mode (must be first argument)\n")); -#ifdef ENABLE_MULTIPLE_NODES - printf(_(" DBNAME database name (mandatory argument in bootstrapping mode)\n")); -#endif - printf(_(" -r FILENAME send stdout and stderr to given file\n")); - printf(_(" -x NUM internal use\n")); - -#ifdef ENABLE_MULTIPLE_NODES - printf(_("\nNode options:\n")); -#ifdef ENABLE_MULTIPLE_NODES - printf(_(" --coordinator start as a Coordinator\n")); - printf(_(" --datanode start as a Datanode\n")); -#endif - printf(_(" --restoremode start to restore existing schema on the new node to be added\n")); - printf(_(" --single_node start as single node\n")); -#else - printf(_("\nNode options:\n")); - printf(_(" --single_node start a single node database. This is default setting.\n")); -#endif - - printf(_("\nPlease read the documentation for the complete list of run-time\n" - "configuration settings and how to set them on the command line or in\n" - "the configuration file.\n")); -#if ((defined(ENABLE_MULTIPLE_NODES)) || (defined(ENABLE_PRIVATEGAUSS))) - printf(_("\nReport bugs to GaussDB support.\n")); -#else - printf(_("\nReport bugs to openGauss community by raising an issue.\n")); -#endif -} - -static void check_root(const char* progname) -{ -#ifndef WIN32 - if (geteuid() == 0) { - write_stderr("\"root\" execution of the gaussdb server is not permitted.\n" - "The server must be started under an unprivileged user ID to prevent\n" - "possible system security compromise. See the documentation for\n" - "more information on how to properly start the server.\n"); - exit(1); - } - - /* - * Also make sure that real and effective uids are the same. Executing as - * a setuid program from a root shell is a security hole, since on many - * platforms a nefarious subroutine could setuid back to root if real uid - * is root. (Since nobody actually uses postgres as a setuid program, - * trying to actively fix this situation seems more trouble than it's - * worth; we'll just expend the effort to check for it.) - */ - if (getuid() != geteuid()) { - write_stderr("%s: real and effective user IDs must match\n", progname); - exit(1); - } -#else /* WIN32 */ - if (pgwin32_is_admin()) { - write_stderr("Execution of gaussdb by a user with administrative permissions is not\n" - "permitted.\n" - "The server must be started under an unprivileged user ID to prevent\n" - "possible system security compromises. See the documentation for\n" - "more information on how to properly start the server.\n"); - exit(1); - } -#endif /* WIN32 */ -} - -static char* get_current_username(const char* progname) -{ -#ifndef WIN32 - struct passwd* pw = NULL; - char* pRet = NULL; - - (void)syscalllockAcquire(&getpwuid_lock); - pw = getpwuid(geteuid()); - if (pw == NULL) { - (void)syscalllockRelease(&getpwuid_lock); - write_stderr("%s: invalid effective UID: %d\n", progname, (int)geteuid()); - exit(1); - } - /* Allocate new memory because later getpwuid() calls can overwrite it. */ - pRet = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), pw->pw_name); - (void)syscalllockRelease(&getpwuid_lock); - return pRet; -#else - unsigned long namesize = 256 /* UNLEN */ + 1; - char* name = NULL; - - name = MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), namesize); - if (!GetUserName(name, &namesize)) { - write_stderr("%s: could not determine user name (GetUserName failed)\n", progname); - exit(1); - } - - return name; -#endif -} - -static void syscall_lock_init(void) -{ - syscalllockInit(&getpwuid_lock); - syscalllockInit(&env_lock); - syscalllockInit(&dlerror_lock); - syscalllockInit(&kerberos_conn_lock); - syscalllockInit(&read_cipher_lock); -} -- 2.34.1 From ca568d528b8bd3b6707cd207ee1a9c8558a043a1 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:18:01 +0800 Subject: [PATCH 54/56] ADD file via upload --- src/gausskernel/process/main/main.cpp | 567 ++++++++++++++++++++++++++ 1 file changed, 567 insertions(+) create mode 100644 src/gausskernel/process/main/main.cpp diff --git a/src/gausskernel/process/main/main.cpp b/src/gausskernel/process/main/main.cpp new file mode 100644 index 000000000..c5e4659d5 --- /dev/null +++ b/src/gausskernel/process/main/main.cpp @@ -0,0 +1,567 @@ +/* + * + * main.cpp + * Stub main() routine for the openGauss executable. + * + * This does some essential startup tasks for any incarnation of openGauss + * (postmaster, standalone backend, standalone bootstrap process, or a + * separately exec'd child of a postmaster) and then dispatches to the + * proper FooMain() routine for the incarnation. + * + * + * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/gausskernel/process/main/main.cpp + * + * ------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "knl/knl_variable.h" + +#include +#include + +#if defined(__alpha) && defined(__osf__) /* no __alpha__ ? */ +#include +#include "machine/hal_sysinfo.h" +#define ASSEMBLER +#include +#undef ASSEMBLER +#endif + +#if defined(__NetBSD__) +#include +#endif + +#include "bootstrap/bootstrap.h" +#include "postmaster/postmaster.h" +#include "miscadmin.h" +#include "storage/ipc.h" +#include "tcop/tcopprot.h" +#include "utils/fmgrtab.h" +#include "utils/help_config.h" +#include "utils/pg_locale.h" +#include "utils/ps_status.h" +#ifdef WIN32 +#include "libpq/pqsignal.h" +#endif + +#include "utils/syscall_lock.h" +#include "gssignal/gs_signal.h" +#include "utils/memutils.h" +#include "utils/mmpool.h" +#include "utils/plog.h" +#include "gstrace/gstrace_infra.h" + +THR_LOCAL bool IsInitdb = false; + +size_t mmap_threshold = (size_t)0xffffffff; + +const char* progname = NULL; + +static void startup_hacks(const char* progname); +static void help(const char* progname); +static void check_root(const char* progname); +static char* get_current_username(const char* progname); +static void syscall_lock_init(void); + +extern int encrypte_main(int argc, char* const argv[]); + +/* + * Any openGauss server process begins execution here. + */ +int main(int argc, char* argv[]) +{ + char* mmap_env = NULL; + syscall_lock_init(); + + // 从环境变量中获取GAUSS_MMAP_THRESHOLD的值,并设置mmap_threshold变量 + mmap_env = gs_getenv_r("GAUSS_MMAP_THRESHOLD"); + if (mmap_env != NULL) { + check_backend_env(mmap_env); + mmap_threshold = (size_t)atol(mmap_env); + } + + // 初始化KNL实例 + knl_instance_init(); + + // 创建增量检查点上下文 + g_instance.increCheckPoint_context = AllocSetContextCreate( + INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE), + "IncreCheckPointContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE, + SHARED_CONTEXT); + + // 创建备机帐号上下文 + g_instance.account_context = AllocSetContextCreate(g_instance.instance_context, + "StandbyAccontContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE, + SHARED_CONTEXT); + + // 创建通信全局内存上下文 + g_instance.comm_cxt.comm_global_mem_cxt = AllocSetContextCreate(g_instance.instance_context, + "CommunnicatorGlobalMemoryContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE, + SHARED_CONTEXT); + + // 创建内置过程内存上下文 + g_instance.builtin_proc_context = AllocSetContextCreate(g_instance.instance_context, + "builtin_procGlobalMemoryContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE, + SHARED_CONTEXT); + + /* + * Fire up essential subsystems: error and memory management + * + * Code after this point is allowed to use elog/ereport, though + * localization of messages may not work right away, and messages won't go + * anywhere but stderr until GUC settings get loaded. + */ + // 初始化内存上下文 + MemoryContextInit(); + + // 设置全局变量PmTopMemoryContext为顶层内存上下文 + PmTopMemoryContext = t_thrd.top_mem_cxt; + + // 初始化线程 + knl_thread_init(MASTER_THREAD); + + // 创建虚拟会话 + t_thrd.fake_session = create_session_context(t_thrd.top_mem_cxt, 0); + t_thrd.fake_session->status = KNL_SESS_FAKE; + + // 将当前会话设置为虚拟会话 + u_sess = t_thrd.fake_session; + + // 设置SelfMemoryContext为默认内存上下文组 + SelfMemoryContext = THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT); + + // 切换到默认内存上下文组 + MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); + + // 获取程序名称 + progname = get_progname(argv[0]); + + + /* + * Platform-specific startup hacks + */ + startup_hacks(progname); + + /* if gaussdb's name is gs_encrypt, so run in encrypte_main() */ + if (!strcmp(progname, "gs_encrypt")) { + return encrypte_main(argc, argv); + } + + // 初始化plog全局内存 + init_plog_global_mem(); + + /* + * Remember the physical location of the initially given argv[] array for + * possible use by ps display. On some platforms, the argv[] storage must + * be overwritten in order to set the process title for ps. In such cases, + * save_ps_display_args makes and returns a new copy of the argv[] array. + * + * save_ps_display_args may also move the environment strings to make + * extra room. Therefore this should be done as early as possible during + * startup, to avoid entanglements with code that might save a getenv() + * result pointer. + */ + argv = save_ps_display_args(argc, argv); + + /* + * If supported on the current platform, set up a handler to be called if + * the backend/postmaster crashes with a fatal signal or exception. + */ + #if defined(WIN32) && defined(HAVE_MINIDUMP_TYPE) + pgwin32_install_crashdump_handler(); + #endif + + /* + * Set up locale information from environment. Note that LC_CTYPE and + * LC_COLLATE will be overridden later from pg_control if we are in an + * already-initialized database. We set them here so that they will be + * available to fill pg_control during initdb. LC_MESSAGES will get set + * later during GUC option processing, but we set it here to allow startup + * error messages to be localized. + */ + set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("gaussdb")); + + #ifdef WIN32 + + /* + * Windows uses codepages rather than the environment, so we work around + * that by querying the environment explicitly first for LC_COLLATE and + * LC_CTYPE. We have to do this because initdb passes those values in the + * environment. If there is nothing there we fall back on the codepage. + */ + { + char* env_locale = NULL; + + if ((env_locale = gs_getenv_r("LC_COLLATE")) != NULL) { + check_backend_env(env_locale); + pg_perm_setlocale(LC_COLLATE, env_locale); + } else + pg_perm_setlocale(LC_COLLATE, ""); + + if ((env_locale = gs_getenv_r("LC_CTYPE")) != NULL) { + check_backend_env(env_locale); + pg_perm_setlocale(LC_CTYPE, env_locale); + } else + pg_perm_setlocale(LC_CTYPE, ""); + } + #else + pg_perm_setlocale(LC_COLLATE, ""); + pg_perm_setlocale(LC_CTYPE, ""); + #endif + + /* + * We keep these set to "C" always, except transiently in pg_locale.c; see + * that file for explanations. + */ + pg_perm_setlocale(LC_MONETARY, "C"); // 将货币格式化设置为"C"语言环境 + pg_perm_setlocale(LC_NUMERIC, "C"); // 将数字格式化设置为"C"语言环境 + pg_perm_setlocale(LC_TIME, "C"); // 将时间格式化设置为"C"语言环境 + + /* + * Now that we have absorbed as much as we wish to from the locale + * environment, remove any LC_ALL setting, so that the environment + * variables installed by pg_perm_setlocale have force. + */ + (void)unsetenv("LC_ALL"); // 移除LC_ALL设置,以确保pg_perm_setlocale设置的环境变量生效 + + /* + * Catch standard options before doing much else + */ + if (argc > 1) { + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { + help(progname); // 显示帮助信息 + exit(0); + } + if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { + puts("gaussdb " DEF_GS_VERSION); // 显示版本号 + exit(0); + } + } + + /* + * Make sure we are not running as root. + */ + check_root(progname); // 检查是否以root用户身份运行 + + /* + * Dispatch to one of various subprograms depending on first argument. + */ + #ifdef WIN32 + + /* + * Start our win32 signal implementation + * + * SubPostmasterMain() will do this for itself, but the remaining modes + * need it here + */ + pgwin32_signal_initialize(); // 在Windows平台上启动信号处理 + + #endif + + t_thrd.mem_cxt.gs_signal_mem_cxt = AllocSetContextCreate( + t_thrd.top_mem_cxt, "gs_signal", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); + if (NULL == t_thrd.mem_cxt.gs_signal_mem_cxt) { + ereport(LOG, (errmsg("could not start a new thread, because of no enough system resource. "))); + proc_exit(1); + } + + /* + * @BuiltinFunc + * Create a global BuiltinFunc object shared among threads + */ + if (g_sorted_funcs[0] == NULL) { + initBuiltinFuncs(); // 初始化内置函数相关的全局变量 + } + + bool isBoot = (argc > 1 && strcmp(argv[1], "--boot") == 0); + if (isBoot) { + IsInitdb = true; + gs_signal_monitor_startup(); // 启动信号监控工作线程 + gs_signal_slots_init(1); // 初始化信号插槽 + (void)gs_signal_unblock_sigusr2(); // 解除SIGUSR2信号的阻塞 + gs_signal_startup_siginfo("AuxiliaryProcessMain"); // 记录启动信息 + BootStrapProcessMain(argc, argv); /* does not return */ + } + + if (argc > 1 && strcmp(argv[1], "--describe-config") == 0) + exit(GucInfoMain()); // 打印GUC参数信息 + + if (argc > 1 && strcmp(argv[1], "--single") == 0) { + IsInitdb = true; + gs_signal_monitor_startup(); // 启动信号监控工作线程 + gs_signal_slots_init(1); // 初始化信号插槽 + (void)gs_signal_unblock_sigusr2(); // 解除SIGUSR2信号的阻塞 + gs_signal_startup_siginfo("PostgresMain"); // 记录启动信息 + + exit(PostgresMain(argc, argv, NULL, get_current_username(progname))); // 进入PostgreSQL主循环 + } + + exit(PostmasterMain(argc, argv)); // 进入Postmaster主循环 + +/* + * Place platform-specific startup hacks here. This is the right + * place to put code that must be executed early in the launch of any new + * server process. Note that this code will NOT be executed when a backend + * or sub-bootstrap process is forked, unless we are in a fork/exec + * environment (ie EXEC_BACKEND is defined). + * + * XXX The need for code here is proof that the platform in question + * is too brain-dead to provide a standard C execution environment + * without help. Avoid adding more here, if you can. + */ +static void startup_hacks(const char* progname) +{ + /* + * On some platforms, unaligned memory accesses result in a kernel trap; + * the default kernel behavior is to emulate the memory access, but this + * results in a significant performance penalty. We want PG never to make + * such unaligned memory accesses, so this code disables the kernel + * emulation: unaligned accesses will result in SIGBUS instead. + */ +#ifdef NOFIXADE + +#if defined(__alpha) /* no __alpha__ ? */ + { + int buffer[] = {SSIN_UACPROC, UAC_SIGBUS | UAC_NOPRINT}; + + if (setsysinfo(SSI_NVPAIRS, buffer, 1, (caddr_t)NULL, (unsigned long)NULL) < 0) + write_stderr("%s: setsysinfo failed: %s\n", progname, gs_strerror(errno)); + } +#endif /* __alpha */ +#endif /* NOFIXADE */ + + /* + * Windows-specific execution environment hacking. + */ +#ifdef WIN32 + { + WSADATA wsaData; + int err; + + /* Make output streams unbuffered by default */ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + /* Prepare Winsock */ + err = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (err != 0) { + write_stderr("%s: WSAStartup failed: %d\n", progname, err); + exit(1); + } + + /* In case of general protection fault, don't show GUI popup box */ + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + } +#endif /* WIN32 */ + + /* Binding static TLS variables for current thread */ + EarlyBindingTLSVariables(); +} + +/* + * Help display should match the options accepted by PostmasterMain() + * and PostgresMain(). + */ +static void help(const char* progname) +{ + printf(_("%s is the gaussdb server.\n\n"), progname); + printf(_("Usage:\n %s [OPTION]...\n\n"), progname); + printf(_("Options:\n")); +#ifdef USE_ASSERT_CHECKING + printf(_(" -A 1|0 enable/disable run-time assert checking\n")); +#endif + printf(_(" -B NBUFFERS number of shared buffers\n")); + printf(_(" -b BINARY UPGRADES flag used for binary upgrades\n")); + printf(_(" -c NAME=VALUE set run-time parameter\n")); + printf(_(" -C NAME print value of run-time parameter, then exit\n")); + printf(_(" -d 1-5 debugging level\n")); + printf(_(" -D DATADIR database directory\n")); + printf(_(" -e use European date input format (DMY)\n")); + printf(_(" -F turn fsync off\n")); + printf(_(" -h HOSTNAME host name or IP address to listen on\n")); + printf(_(" -i enable TCP/IP connections\n")); + printf(_(" -k DIRECTORY Unix-domain socket location\n")); +#ifdef USE_SSL + printf(_(" -l enable SSL connections\n")); +#endif + printf(_(" -N MAX-CONNECT maximum number of allowed connections\n")); + printf(_(" -M SERVERMODE the database start as the appointed server mode\n")); + + printf(_(" -o OPTIONS pass \"OPTIONS\" to each server process (obsolete)\n")); + printf(_(" -p PORT port number to listen on\n")); +#ifdef ENABLE_MULTIPLE_NODES + printf(_(" -R indicate run as xlogreiver.Only used with -M standby\n")); +#endif + printf(_(" -s show statistics after each query\n")); + printf(_(" -S WORK-MEM set amount of memory for sorts (in kB)\n")); + printf(_(" -u NUM set the num of kernel version before upgrade\n")); + printf(_(" -V, --version output version information, then exit\n")); + printf(_(" --NAME=VALUE set run-time parameter\n")); + printf(_(" --describe-config describe configuration parameters, then exit\n")); + printf(_(" --securitymode allow database system run in security mode\n")); + printf(_(" --single_node A SingleDN mode is being activated\n")); + printf(_(" -?, --help show this help, then exit\n")); + + printf(_("\nServer mode:\n")); + printf(_(" primary database system starts as a primary server, send xlog to standby server\n")); + printf(_(" standby database system starts as a standby server, receive xlog from primary server\n")); + printf(_(" pending database system starts as a pending server, wait for promoting to primary or " + "demoting to standby\n")); +#ifdef ENABLE_MULTIPLE_NODES + printf(_(" fenced database system starts a fenced master process, serve UDF execution in secure mode " + "(run separately from Gaussdb process)\n")); +#endif + + printf(_("\nDeveloper options:\n")); + printf(_(" -f s|i|n|m|h forbid use of some plan types\n")); + printf(_(" -n do not reinitialize shared memory after abnormal exit\n")); + printf(_(" -O allow system table structure changes\n")); + printf(_(" -P disable system indexes\n")); + printf(_(" -t pa|pl|ex show timings after each query\n")); + printf(_(" -T send SIGSTOP to all backend processes if one dies\n")); + printf(_(" -W NUM wait NUM seconds to allow attach from a debugger\n")); + printf(_(" --localxid use local transaction id (used only by gs_initdb)\n")); + + printf(_("\nOptions for single-user mode:\n")); + printf(_(" --single selects single-user mode (must be first argument)\n")); + printf(_(" DBNAME database name (defaults to user name)\n")); + printf(_(" -d 0-5 override debugging level\n")); + printf(_(" -E echo statement before execution\n")); + printf(_(" -j do not use newline as interactive query delimiter\n")); + printf(_(" -r FILENAME send stdout and stderr to given file\n")); + + printf(_("\nOptions for bootstrapping mode:\n")); + printf(_(" --boot selects bootstrapping mode (must be first argument)\n")); +#ifdef ENABLE_MULTIPLE_NODES + printf(_(" DBNAME database name (mandatory argument in bootstrapping mode)\n")); +#endif + printf(_(" -r FILENAME send stdout and stderr to given file\n")); + printf(_(" -x NUM internal use\n")); + +#ifdef ENABLE_MULTIPLE_NODES + printf(_("\nNode options:\n")); +#ifdef ENABLE_MULTIPLE_NODES + printf(_(" --coordinator start as a Coordinator\n")); + printf(_(" --datanode start as a Datanode\n")); +#endif + printf(_(" --restoremode start to restore existing schema on the new node to be added\n")); + printf(_(" --single_node start as single node\n")); +#else + printf(_("\nNode options:\n")); + printf(_(" --single_node start a single node database. This is default setting.\n")); +#endif + + printf(_("\nPlease read the documentation for the complete list of run-time\n" + "configuration settings and how to set them on the command line or in\n" + "the configuration file.\n")); +#if ((defined(ENABLE_MULTIPLE_NODES)) || (defined(ENABLE_PRIVATEGAUSS))) + printf(_("\nReport bugs to GaussDB support.\n")); +#else + printf(_("\nReport bugs to openGauss community by raising an issue.\n")); +#endif +} + +static void check_root(const char* progname) +{ +#ifndef WIN32 + if (geteuid() == 0) { + write_stderr("\"root\" execution of the gaussdb server is not permitted.\n" + "The server must be started under an unprivileged user ID to prevent\n" + "possible system security compromise. See the documentation for\n" + "more information on how to properly start the server.\n"); + exit(1); + } + + /* + * Also make sure that real and effective uids are the same. Executing as + * a setuid program from a root shell is a security hole, since on many + * platforms a nefarious subroutine could setuid back to root if real uid + * is root. (Since nobody actually uses postgres as a setuid program, + * trying to actively fix this situation seems more trouble than it's + * worth; we'll just expend the effort to check for it.) + */ + if (getuid() != geteuid()) { + write_stderr("%s: real and effective user IDs must match\n", progname); + exit(1); + } +#else /* WIN32 */ + if (pgwin32_is_admin()) { + write_stderr("Execution of gaussdb by a user with administrative permissions is not\n" + "permitted.\n" + "The server must be started under an unprivileged user ID to prevent\n" + "possible system security compromises. See the documentation for\n" + "more information on how to properly start the server.\n"); + exit(1); + } +#endif /* WIN32 */ +} +/** + * get_current_username - 获取当前操作系统的用户名 + * @progname: 程序名称 + * + * 返回当前操作系统的用户名。 + */ + static char* get_current_username(const char* progname) + { + #ifndef WIN32 // 非Windows平台代码 + struct passwd* pw = NULL; + char* pRet = NULL; + + (void)syscalllockAcquire(&getpwuid_lock); // 获取线程锁 + pw = getpwuid(geteuid()); // 获取与实际用户ID关联的密码记录 + if (pw == NULL) { // 如果无法获取则报错 + (void)syscalllockRelease(&getpwuid_lock); + write_stderr("%s: invalid effective UID: %d\n", progname, (int)geteuid()); + exit(1); + } + /* Allocate new memory because later getpwuid() calls can overwrite it. */ + pRet = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), pw->pw_name); // 分配新内存来储存用户名,并返回该内存地址 + (void)syscalllockRelease(&getpwuid_lock); // 释放线程锁 + return pRet; // 返回用户名 + #else // Windows平台代码 + unsigned long namesize = 256 /* UNLEN */ + 1; + char* name = NULL; + + name = MemoryContextAlloc( + SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB), namesize); // 分配内存来储存用户名,返回该内存地址 + if (!GetUserName(name, &namesize)) { // 获取当前用户的用户名 + write_stderr("%s: could not determine user name (GetUserName failed)\n", progname); + exit(1); + } + + return name; // 返回用户名 + #endif + } + + /** + * syscall_lock_init - 初始化系统调用锁 + * + * 初始化用于保护某些系统调用(如 getenv)的线程锁。 + */ + static void syscall_lock_init(void) + { + syscalllockInit(&getpwuid_lock); // 初始化获取用户ID对应密码记录的线程锁 + syscalllockInit(&env_lock); // 初始化 getenv 的线程锁 + syscalllockInit(&dlerror_lock); // 初始化 dlerror 的线程锁 + syscalllockInit(&kerberos_conn_lock); // 初始化 Kerberos 连接相关的线程锁 + syscalllockInit(&read_cipher_lock); // 初始化加密算法相关的线程锁 + } + -- 2.34.1 From 68732254fcd7d186ae3c205e60b2ccd2687808b3 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:18:56 +0800 Subject: [PATCH 55/56] Delete 'src/gausskernel/process/postmaster/alarmchecker.cpp' --- .../process/postmaster/alarmchecker.cpp | 306 ------------------ 1 file changed, 306 deletions(-) delete mode 100644 src/gausskernel/process/postmaster/alarmchecker.cpp diff --git a/src/gausskernel/process/postmaster/alarmchecker.cpp b/src/gausskernel/process/postmaster/alarmchecker.cpp deleted file mode 100644 index 2ffe93696..000000000 --- a/src/gausskernel/process/postmaster/alarmchecker.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2020 Huawei Technologies Co.,Ltd. - * - * 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. - * ------------------------------------------------------------------------- - * - * alarmchecker.cpp - * - * openGauss Alarm checker thread Implementation - * - * IDENTIFICATION - * src/gausskernel/process/postmaster/alarmchecker.cpp - * - * ------------------------------------------------------------------------- - */ -#include "postgres.h" -#include "knl/knl_variable.h" - -#include -#include -#include -#include - -#include "libpq/pqsignal.h" -#include "miscadmin.h" -#include "postmaster/fork_process.h" -#include "postmaster/postmaster.h" -#include "storage/ipc.h" -#include "storage/latch.h" -#include "storage/proc.h" -#include "utils/guc.h" -#include "utils/memutils.h" -#include "utils/ps_status.h" -#include "alarm/alarm.h" -#include "utils/elog.h" -#include "pgxc/pgxc.h" -#include "postmaster/alarmchecker.h" -#include "gssignal/gs_signal.h" -#include "replication/walsender.h" - -// declare the global variable of alarm module -int g_alarmReportInterval; -char g_alarmComponentPath[MAXPGPATH]; -int g_alarmReportMaxCount; -/* seconds, interval of alarm check loop. */ -static const int AlarmCheckInterval = 1; - -bool enable_alarm = false; - -static Alarm* DataInstAlarmList = NULL; - -static int DataInstAlarmListSize = 0; - -AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); - -static void DataInstAlarmItemInitialize(void); - -static void acSighupHandler(SIGNAL_ARGS); -static void acSigquitHandler(SIGNAL_ARGS); - -extern AlarmCheckResult DataInstArchChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); -extern AlarmCheckResult ConnAuthMethodChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); -extern AlarmCheckResult DataInstConnToGTMChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); - -void DataInstAlarmItemInitialize(void) -{ - DataInstAlarmListSize = 6; - DataInstAlarmList = (Alarm*)AlarmAlloc(sizeof(Alarm) * DataInstAlarmListSize); - if (NULL == DataInstAlarmList) { - AlarmLog(ALM_LOG, "Out of memory: DataInstAlarmItemInitialize failed."); - exit(1); - } - // ALM_AI_MissingDataInstDataOrRedoDir - AlarmItemInitialize( - &(DataInstAlarmList[0]), ALM_AI_MissingDataInstDataOrRedoDir, ALM_AS_Normal, DataOrRedoDirNotExistChecker); - // ALM_AI_MissingDataInstWalSegmt - AlarmItemInitialize( - &(DataInstAlarmList[1]), ALM_AI_MissingDataInstWalSegmt, ALM_AS_Normal, WalSegmentsRemovedChecker); - // ALM_AI_TooManyDataInstConn - AlarmItemInitialize(&(DataInstAlarmList[2]), ALM_AI_TooManyDataInstConn, ALM_AS_Normal, ConnectionOverloadChecker); - // ALM_AI_AbnormalDataInstArch - AlarmItemInitialize(&(DataInstAlarmList[3]), ALM_AI_AbnormalDataInstArch, ALM_AS_Normal, DataInstArchChecker); - // ALM_AI_AbnormalDataInstConnAuthMethod - AlarmItemInitialize( - &(DataInstAlarmList[4]), ALM_AI_AbnormalDataInstConnAuthMethod, ALM_AS_Normal, ConnAuthMethodChecker); - // ALM_AI_AbnormalDataInstConnToGTM - AlarmItemInitialize( - &(DataInstAlarmList[5]), ALM_AI_AbnormalDataInstConnToGTM, ALM_AS_Normal, DataInstConnToGTMChecker); -} - -ThreadId startAlarmChecker(void) -{ - if (!IsPostmasterEnvironment || !enable_alarm) { - return 0; - } - - return initialize_util_thread(ALARMCHECK); -} - -NON_EXEC_STATIC void AlarmCheckerMain() -{ - - /* we are a postmaster subprocess now */ - IsUnderPostmaster = true; - - /* reset t_thrd.proc_cxt.MyProcPid */ - t_thrd.proc_cxt.MyProcPid = gs_thread_self(); - - /* record Start Time for logging */ - t_thrd.proc_cxt.MyStartTime = time(NULL); - - /* reord my name */ - t_thrd.proc_cxt.MyProgName = "AlarmChecker"; - - /* Identify myself via ps */ - init_ps_display("AlarmChecker", "", "", ""); - - AlarmLog(ALM_LOG, "alarm checker started."); - - InitializeLatchSupport(); /* needed for latch waits */ - - /* Initialize private latch for use by signal handlers */ - InitLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); - - /* - * Properly accept or ignore signals the postmaster might send us - * - * Note: we deliberately ignore SIGTERM, because during a standard Unix - * system shutdown cycle, init will SIGTERM all processes at once. We - * want to wait for the backends to exit, whereupon the postmaster will - * tell us it's okay to shut down (via SIGUSR2). - */ - (void)gspqsignal(SIGHUP, acSighupHandler); /* set flag to read config file */ - (void)gspqsignal(SIGINT, SIG_IGN); - (void)gspqsignal(SIGTERM, SIG_IGN); - (void)gspqsignal(SIGQUIT, acSigquitHandler); - (void)gspqsignal(SIGALRM, SIG_IGN); - (void)gspqsignal(SIGPIPE, SIG_IGN); - (void)gspqsignal(SIGUSR1, SIG_IGN); - (void)gspqsignal(SIGUSR2, SIG_IGN); - - /* - * Reset some signals that are accepted by postmaster but not here - */ - (void)gspqsignal(SIGCHLD, SIG_DFL); - (void)gspqsignal(SIGTTIN, SIG_DFL); - (void)gspqsignal(SIGTTOU, SIG_DFL); - (void)gspqsignal(SIGCONT, SIG_DFL); - (void)gspqsignal(SIGWINCH, SIG_DFL); - - gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); - (void)gs_signal_unblock_sigusr2(); - - /* all is done info top memory context. */ - (void)MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); - - DataInstAlarmItemInitialize(); - - for (;;) { - /* Clear any already-pending wakeups */ - ResetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); - - /* the normal shutdown case */ - if (t_thrd.alarm_cxt.gotSigdie) - break; - - /* - * reload the postgresql.conf - */ - if (t_thrd.alarm_cxt.gotSighup) { - t_thrd.alarm_cxt.gotSighup = false; - ProcessConfigFile(PGC_SIGHUP); - } - - AlarmCheckerLoop(DataInstAlarmList, DataInstAlarmListSize); - - /* - * Sleep until there's something to do - */ - (void)WaitLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch, WL_LATCH_SET | WL_TIMEOUT, AlarmCheckInterval * 1000); - } - - AlarmLog(ALM_LOG, "alarm checker shutting down..."); - - proc_exit(0); -} - -/* - * signal handle functions - */ -/* - * @@GaussDB@@ - * Brief : handle SIGHUP signal and set t_thrd.alarm_cxt.gotSighup flag - * Description : - * Notes : - */ -static void acSighupHandler(SIGNAL_ARGS) -{ - int save_errno = errno; - - t_thrd.alarm_cxt.gotSighup = true; - - SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); - - errno = save_errno; -} - -/* - * @@GaussDB@@ - * Brief : handle SIGTERM, SIGINT signal and set t_thrd.alarm_cxt.gotSigdie flag - * Description : - * Notes : - */ -static void acSigquitHandler(SIGNAL_ARGS) -{ - int save_errno = errno; - - t_thrd.alarm_cxt.gotSigdie = true; - - SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); - - errno = save_errno; -} - -bool isDirExist(const char* dir) -{ - struct stat stat_buf; - - if (stat(dir, &stat_buf) != 0) - return false; - - if (!S_ISDIR(stat_buf.st_mode)) - return false; - -#if !defined(WIN32) && !defined(__CYGWIN__) - - if (stat_buf.st_uid != geteuid()) - return false; - - if ((stat_buf.st_mode & S_IRWXU) != S_IRWXU) - return false; - -#endif - - return true; -} - -AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam) -{ - if (isDirExist(t_thrd.proc_cxt.DataDir) && isDirExist("pg_xlog")) { - // fill the alarm message - WriteAlarmAdditionalInfo(additionalParam, - g_instance.attr.attr_common.PGXCNodeName, - "", - "", - alarm, - ALM_AT_Resume, - g_instance.attr.attr_common.PGXCNodeName); - return ALM_ACR_Normal; - } else { - // fill the alarm message - WriteAlarmAdditionalInfo(additionalParam, - g_instance.attr.attr_common.PGXCNodeName, - "", - "", - alarm, - ALM_AT_Fault, - g_instance.attr.attr_common.PGXCNodeName); - return ALM_ACR_Abnormal; - } -} - -/* implementation of alarm module. */ -void AlarmFree(void* pointer) -{ - if (pointer != NULL) - pfree(pointer); -} - -void* AlarmAlloc(size_t size) -{ - return palloc(size); -} - -void AlarmLogImplementation(int level, const char* prefix, const char* logtext) -{ - switch (level) { - case ALM_DEBUG: - ereport(DEBUG3, (errmsg("%s%s", prefix, logtext))); - break; - case ALM_LOG: - ereport(LOG, (errmsg("%s%s", prefix, logtext))); - break; - default: - break; - } -} -- 2.34.1 From 064464c40d26b2e8fe68016d7026e5ab725e1a38 Mon Sep 17 00:00:00 2001 From: makqmlwy5 Date: Wed, 4 Oct 2023 23:23:07 +0800 Subject: [PATCH 56/56] ADD file via upload --- .../process/postmaster/alarmchecker.cpp | 251 ++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 src/gausskernel/process/postmaster/alarmchecker.cpp diff --git a/src/gausskernel/process/postmaster/alarmchecker.cpp b/src/gausskernel/process/postmaster/alarmchecker.cpp new file mode 100644 index 000000000..87c8fddc7 --- /dev/null +++ b/src/gausskernel/process/postmaster/alarmchecker.cpp @@ -0,0 +1,251 @@ +//这些是各种头文件的引用,包含了一些系统库、PostgreSQL内部模块和自定义的模块 +#include "postgres.h" +#include "knl/knl_variable.h" + +#include +#include +#include +#include + +#include "libpq/pqsignal.h" +#include "miscadmin.h" +#include "postmaster/fork_process.h" +#include "postmaster/postmaster.h" +#include "storage/ipc.h" +#include "storage/latch.h" +#include "storage/proc.h" +#include "utils/guc.h" +#include "utils/memutils.h" +#include "utils/ps_status.h" +#include "alarm/alarm.h" +#include "utils/elog.h" +#include "pgxc/pgxc.h" +#include "postmaster/alarmchecker.h" +#include "gssignal/gs_signal.h" +#include "replication/walsender.h" +//定义一些全局变量 +int g_alarmReportInterval;//报警上报的时间间隔 +char g_alarmComponentPath[MAXPGPATH];// 报警组件的路径 +int g_alarmReportMaxCount;//报警上报的最大次数 + +static const int AlarmCheckInterval = 1;//定义了一个静态常量 AlarmCheckInterval,值为1,表示报警检查的时间间隔(单位:秒) +bool enable_alarm = false;//定义并初始化了一个bool型变量 enable_alarm,初始值为 false,表示是否启用报警功能。 +//定义了静态变量 DataInstAlarmList 和 DataInstAlarmListSize,用于存储报警项的列表和列表大小 +static Alarm* DataInstAlarmList = NULL; +static int DataInstAlarmListSize = 0; + +AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); + +static void DataInstAlarmItemInitialize(void); + +static void acSighupHandler(SIGNAL_ARGS); +static void acSigquitHandler(SIGNAL_ARGS); + +extern AlarmCheckResult DataInstArchChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); +extern AlarmCheckResult ConnAuthMethodChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); +extern AlarmCheckResult DataInstConnToGTMChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam); +//用于初始化报警项列表 +void DataInstAlarmItemInitialize(void) +{ + DataInstAlarmListSize = 6;//设置列表大小为6 + DataInstAlarmList = (Alarm*)AlarmAlloc(sizeof(Alarm) * DataInstAlarmListSize);//给 DataInstAlarmList分配内存 + //如果分配失败 + if (NULL == DataInstAlarmList) { + AlarmLog(ALM_LOG, "Out of memory: DataInstAlarmItemInitialize failed.");//记录错误日志并退出程序 + exit(1); + } + //调用函数AlarmItemInitialize对每个报警项进行初始化,每个报警项由一个Alarm结构体表示,包含报警项的类型、报警状态和报警检查函数 + // ALM_AI_MissingDataInstDataOrRedoDir + AlarmItemInitialize( + &(DataInstAlarmList[0]), ALM_AI_MissingDataInstDataOrRedoDir, ALM_AS_Normal, DataOrRedoDirNotExistChecker); + // ALM_AI_MissingDataInstWalSegmt + AlarmItemInitialize( + &(DataInstAlarmList[1]), ALM_AI_MissingDataInstWalSegmt, ALM_AS_Normal, WalSegmentsRemovedChecker); + // ALM_AI_TooManyDataInstConn + AlarmItemInitialize(&(DataInstAlarmList[2]), ALM_AI_TooManyDataInstConn, ALM_AS_Normal, ConnectionOverloadChecker); + // ALM_AI_AbnormalDataInstArch + AlarmItemInitialize(&(DataInstAlarmList[3]), ALM_AI_AbnormalDataInstArch, ALM_AS_Normal, DataInstArchChecker); + // ALM_AI_AbnormalDataInstConnAuthMethod + AlarmItemInitialize( + &(DataInstAlarmList[4]), ALM_AI_AbnormalDataInstConnAuthMethod, ALM_AS_Normal, ConnAuthMethodChecker); + // ALM_AI_AbnormalDataInstConnToGTM + AlarmItemInitialize( + &(DataInstAlarmList[5]), ALM_AI_AbnormalDataInstConnToGTM, ALM_AS_Normal, DataInstConnToGTMChecker); +} +//用于启动报警检查进程 +ThreadId startAlarmChecker(void) +{ + //看是否处于Postmaster环境并且是否启用了报警功能 + if (!IsPostmasterEnvironment || !enable_alarm) {//如果不满足条件,则返回0。 + return 0; + } + //否则,调用initialize_util_thread函数来启动报警检查器线程。 + return initialize_util_thread(ALARMCHECK); +} +//定义了一个名为AlarmCheckerMain的静态函数。 +//NON_EXEC_STATIC用于指定函数不会被直接执行,而是作为子进程在PostgreSQL中运行。 +NON_EXEC_STATIC void AlarmCheckerMain() +{ + IsUnderPostmaster = true;//设置变量IsUnderPostmaster为true,表示当前进程是一个后台进程 + + t_thrd.proc_cxt.MyProcPid = gs_thread_self();//重置t_thrd.proc_cxt.MyProcPid为当前线程 + + t_thrd.proc_cxt.MyStartTime = time(NULL);//记录当前时间为t_thrd.proc_cxt.MyStartTime + + t_thrd.proc_cxt.MyProgName = "AlarmChecker";//将当前进程名设置为AlarmChecker + + init_ps_display("AlarmChecker", "", "", "");//调用函数init_ps_display来显示初始化进程状态 + //调用函数AlarmLog记录日志,表示报警检查程序已经启动 + AlarmLog(ALM_LOG, "alarm checker started."); + //调用函数InitializeLatchSupport来初始化latch支持。 + InitializeLatchSupport(); /* needed for latch waits */ + /* Initialize private latch for use by signal handlers */ + //初始化一个latch对象,用于处理信号处理程序中的同步等待。 + InitLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); + //使用gspqsignal函数设置了一些信号的处理行为,如SIGHUP、SIGINT、SIGTERM等。 + (void)gspqsignal(SIGHUP, acSighupHandler); + (void)gspqsignal(SIGINT, SIG_IGN); + (void)gspqsignal(SIGTERM, SIG_IGN); + (void)gspqsignal(SIGQUIT, acSigquitHandler); + (void)gspqsignal(SIGALRM, SIG_IGN); + (void)gspqsignal(SIGPIPE, SIG_IGN); + (void)gspqsignal(SIGUSR1, SIG_IGN); + (void)gspqsignal(SIGUSR2, SIG_IGN); + //对于某些信号通过SIG_IGN忽略,而对于其他信号通过SIG_DFL恢复为默认行为。 + + (void)gspqsignal(SIGCHLD, SIG_DFL); + (void)gspqsignal(SIGTTIN, SIG_DFL); + (void)gspqsignal(SIGTTOU, SIG_DFL); + (void)gspqsignal(SIGCONT, SIG_DFL); + (void)gspqsignal(SIGWINCH, SIG_DFL); + //设置信号掩码使得非阻塞的信号可用 + gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); + //解除SIGUSR2信号的阻塞 + (void)gs_signal_unblock_sigusr2(); + //切换到默认内存上下文中 + (void)MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); + + //调用初始化报警项列表 + DataInstAlarmItemInitialize(); + for (;;) { + //清除任何已经挂起的唤醒信号 + ResetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); + if (t_thrd.alarm_cxt.gotSigdie)// 接收到终止信号,跳出循环,结束线程 + break; + //检查是否接收到了SIGDIE信号 + if (t_thrd.alarm_cxt.gotSighup) { + t_thrd.alarm_cxt.gotSighup = false;//接收到,重新加载postgresql.conf配置文件 + ProcessConfigFile(PGC_SIGHUP); + } + //进行报警检查 + AlarmCheckerLoop(DataInstAlarmList, DataInstAlarmListSize); + //进入休眠状态,等待下一次循环 + (void)WaitLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch, WL_LATCH_SET | WL_TIMEOUT, AlarmCheckInterval * 1000); + } + //调用AlarmLog函数记录日志,表示报警检查程序即将关闭 + AlarmLog(ALM_LOG, "alarm checker shutting down..."); + //结束进程 + proc_exit(0); +} +//定义了一个名为acSighupHandler的静态函数,用于处理SIGHUP信号,SIGNAL_ARGS是用于接收信号处理程序的参数。 +static void acSighupHandler(SIGNAL_ARGS) +{ + int save_errno = errno;//保存当前错误码 + + t_thrd.alarm_cxt.gotSighup = true;//置为true,表示接收到了SIGHUP信号 + //调用SetLatch函数设置latch对象,以唤醒等待该latch的进程 + SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); + //恢复之前保存的错误码 + errno = save_errno; +} +//定义了一个名为acSigquitHandler的静态函数,用于处理SIGQUIT信号。SIGNAL_ARGS用于接收信号处理程序的参数。 +static void acSigquitHandler(SIGNAL_ARGS) +{ + int save_errno = errno;//保存当前错误码 + + t_thrd.alarm_cxt.gotSigdie = true;//置为true,表示接收到了SIGQUIT信号 + //调用SetLatch函数设置latch对象,以唤醒等待该latch的进程 + SetLatch(&t_thrd.alarm_cxt.AlarmCheckerLatch); + //恢复之前保存的错误码 + errno = save_errno; +} +//定义了一个名为isDirExist的函数,用于判断指定目录是否存在。参数dir表示要检查的目录路径。 +bool isDirExist(const char* dir) +{ + struct stat stat_buf;//定义了一个stat结构体用于存储目录的属性信息 + //使用stat函数获取目录的属性信息 + if (stat(dir, &stat_buf) != 0)//如果返回值不为0 + return false;//获取失败,说明目录不存在,返回false + //判断获取到的目录的属性中的st_mode字段是否为目录类型 + if (!S_ISDIR(stat_buf.st_mode))//若不是目录类型,则返回false + return false; + //这是对非Windows和非Cygwin系统上的额外检查: +#if !defined(WIN32) && !defined(__CYGWIN__) + + if (stat_buf.st_uid != geteuid())//检查目录的拥有者是否与当前用户ID相同 + return false; + + if ((stat_buf.st_mode & S_IRWXU) != S_IRWXU)//检查目录的权限是否设置为用户可读、写、执行的权限 + return false; + +#endif + //目录存在且满足所有条件,返回true,否则返回false + return true; +} +//定义了一个名为DataOrRedoDirNotExistChecker的函数,用于检查数据目录和pg_xlog目录是否存在。 +//有两个参数:alarm表示报警对象,additionalParam表示额外的参数。 +AlarmCheckResult DataOrRedoDirNotExistChecker(Alarm* alarm, AlarmAdditionalParam* additionalParam) +{ + //调用isDirExist函数判断数据目录和pg_xlog目录是否都存在 + if (isDirExist(t_thrd.proc_cxt.DataDir) && isDirExist("pg_xlog")) {//如果两个目录都存在,则执行以下操作: + // fill the alarm message //- 使用WriteAlarmAdditionalInfo函数填充报警消息的额外信息。 + WriteAlarmAdditionalInfo(additionalParam, + g_instance.attr.attr_common.PGXCNodeName, + "", + "", + alarm, + ALM_AT_Resume, + g_instance.attr.attr_common.PGXCNodeName); + return ALM_ACR_Normal;//- 返回ALM_ACR_Normal,表示检查结果正常。 + } else { //如果两个目录有任何一个不存在,则执行以下操作 + // fill the alarm message + WriteAlarmAdditionalInfo(additionalParam, //使用WriteAlarmAdditionalInfo函数填充报警消息的额外信息 + g_instance.attr.attr_common.PGXCNodeName, + "", + "", + alarm, + ALM_AT_Fault, + g_instance.attr.attr_common.PGXCNodeName); + return ALM_ACR_Abnormal;// 返回ALM_ACR_Abnormal,表示检查结果异常 + } +} + +/* implementation of alarm module. */ +//定义了一个名为AlarmFree的函数,用于释放内存。pointer表示要释放的内存指针。 +void AlarmFree(void* pointer) +{ + //检查指针是否为空 + if (pointer != NULL) + pfree(pointer);//如果不为空,则调用pfree函数释放内存 +} +//定义了一个名为AlarmAlloc的函数,用于分配内存。size表示要分配的内存大小 +void* AlarmAlloc(size_t size) +{ + return palloc(size);//调用palloc函数分配内存,并将分配的内存地址返回 +} +//定义了一个名为AlarmLogImplementation的函数,用于记录报警日志。 +//有三个参数:level表示日志级别,prefix表示日志前缀,logtext表示要记录的日志文本。 +void AlarmLogImplementation(int level, const char* prefix, const char* logtext) +{ + //使用switch语句根据日志级别执行不同的操作。 + switch (level) { + case ALM_DEBUG://如果级别是ALM_DEBUG + ereport(DEBUG3, (errmsg("%s%s", prefix, logtext)));//调用ereport函数使用DEBUG3级别记录日志 + break; + case ALM_LOG://如果级别是ALM_LOG + ereport(LOG, (errmsg("%s%s", prefix, logtext)));//调用ereport函数使用LOG级别记录日志。 + break; + default: + break;//其他情况不执行任何操作。 + } +} -- 2.34.1