diff --git a/frameworks/config_policy/src/config_policy_utils.c b/frameworks/config_policy/src/config_policy_utils.c index dce0a93497910d462ba1bc579b06691ff70c8789..15e93b2b3577bf10573edd8211dd1d9c2ec1aaff 100644 --- a/frameworks/config_policy/src/config_policy_utils.c +++ b/frameworks/config_policy/src/config_policy_utils.c @@ -15,7 +15,9 @@ #include "config_policy_utils.h" +#include #include +#include #include #include "config_policy_impl.h" @@ -23,6 +25,336 @@ #include "init_param.h" #endif +const int MIN_APPEND_LEN = 32; +// ':' split different x rules, example:":relPath,mode[,extra][:]" +// ',' split different param for x rules +// ":-" split for key:-value +// exampe:"etc/xml/config.xml,10:etc/xml/config1.xml,100,etc/carrier/${key:-value}" +const char SEP_FOR_X_RULE = ':'; +const char SEP_FOR_X_PARAM = ','; +const char *SEP_FOR_X_VALUE = ":-"; + +typedef struct { + size_t size; // alloced buffer size of p + size_t strLen; // strlen(p), less than size + char *p; // just init to NULL, by malloc +} StringHolder; + +typedef struct { + int segCount; // count of char * in segs + char *orgStr; + char *segs[1]; +} SplitedStr; + +/** + * query function, the memory is alloced in function + * @return value, or NULL if not exist or strlen(value) is 0 + * @NOTICE caller should free the returned value + */ +typedef char *(*QueryFunc)(const char *key); +static char *ExpandStr(char *src, const char *def, QueryFunc queryFunc); + +static inline int Min(int a, int b) +{ + return a < b ? a : b; +} + +static inline int Max(int a, int b) +{ + return a > b ? a : b; +} + +static void FreeIf(void *p) +{ + if (p != NULL) { + free(p); + } +} + +static char *CustGetSystemParam(const char *name) +{ + char *value = NULL; + unsigned int len = 0; + + if (SystemGetParameter(name, NULL, &len) != 0 || len == 0) { + return NULL; + } + value = (char *)calloc(len, sizeof(char)); + if (value != NULL && SystemGetParameter(name, value, &len) == 0 && value[0]) { + return value; + } + FreeIf(value); + return NULL; +} + +static char *GetOpkeyPath(int type) +{ + char *result = NULL; + const char *opKeyDir = "etc/carrier/"; + const char *opKeyName = CUST_OPKEY0; + if (type == FOLLOWX_MODE_SIM_1) { + opKeyName = CUST_OPKEY0; + } else if (type == FOLLOWX_MODE_SIM_2) { + opKeyName = CUST_OPKEY1; + } else { + unsigned int len = 0; + if (SystemGetParameter(CUST_OPKEY0, NULL, &len) == 0 && len > 0) { + opKeyName = CUST_OPKEY0; + } else if (SystemGetParameter(CUST_OPKEY1, NULL, &len) == 0 && len > 0) { + opKeyName = CUST_OPKEY1; + } + } + char *opKeyValue = CustGetSystemParam(opKeyName); + if (opKeyValue == NULL) { + return NULL; + } + size_t bufSize = strlen(opKeyDir) + strlen(opKeyValue) + 1; + bufSize = Min(bufSize, MAX_PATH_LEN); + result = (char *)calloc(bufSize, sizeof(char)); + if (result != NULL && sprintf_s(result, bufSize, "%s%s", opKeyDir, opKeyValue) > 0) { + FreeIf(opKeyValue); + return result; + } + FreeIf(opKeyValue); + FreeIf(result); + return NULL; +} + +static SplitedStr *SplitStr(char *str, char delim) +{ + int segCount = 1; + for (char *p = str; *p != '\0'; p++) { + *p == delim ? segCount++ : 0; + } + SplitedStr *result = (SplitedStr *)calloc(sizeof(SplitedStr) + sizeof(char *) * segCount, 1); + if (result == NULL) { + return NULL; + } + result->segCount = segCount; + result->orgStr = str; + + char *nextDelim = NULL; + char delimStr[] = {delim, 0}; + result->segs[0] = strtok_s(str, delimStr, &nextDelim); + for (int index = 1; index < segCount; index++) { + result->segs[index] = strtok_s(NULL, delimStr, &nextDelim); + } + return result; +} + +static void FreeSplitedStr(SplitedStr *p) +{ + if (p != NULL) { + FreeIf(p->orgStr); + p->orgStr = NULL; + free(p); + } +} + +// get follow x rule from param variant +// *mode: read from contains param variant, mode is output param. +// return: extra path rule. +// followPath format ":relPath,mode[,extra][:]" +// example: ":etc/xml/config.xml,10:etc/xml/config1.xml,100,etc/carrier/${key:-value}" +static char *GetFollowXRule(const char *relPath, int *mode) +{ + char *followRule = CustGetSystemParam(CUST_FOLLOW_X_RULES); + if (followRule == NULL) { + return NULL; + } + + size_t bufSize = strlen(relPath) + sizeof(":,") + 1; + bufSize = Min(bufSize, MAX_PATH_LEN); + char *search = (char *)calloc(bufSize, sizeof(char)); + if (search == NULL || sprintf_s(search, bufSize, ":%s,", relPath) == -1) { + FreeIf(search); + return NULL; + } + + char *addPath = NULL; + char *item = strstr(followRule, search); + if (item) { + item++; // skip delim ':', goto ":relPath,mode[,extra][:]" + char *endItem = strchr(item, SEP_FOR_X_RULE); + char *nextItem = endItem + 1; + while (endItem && nextItem && *nextItem == '-') { + endItem = strchr(nextItem, SEP_FOR_X_RULE); + nextItem = endItem + 1; + } + if (endItem) { + *endItem = '\0'; + } + char *modeStr = strchr(item, SEP_FOR_X_PARAM); + if (modeStr) { + modeStr++; + *mode = atoi(modeStr); + } + if (*mode == FOLLOWX_MODE_USER_DEFINE) { + addPath = strchr(modeStr, SEP_FOR_X_PARAM); + if (addPath) { + addPath++; // skip ',' get extra info + } + } + } + + char *result = (addPath && *addPath) ? strdup(addPath) : NULL; + FreeIf(followRule); + FreeIf(search); + return result; +} + +static SplitedStr *GetFollowXPathByMode(const char *relPath, int followMode, const char *extra) +{ + char *followXPath = NULL; + char *modePathFromCfg = NULL; + const char *extraPath = extra; + if (followMode == FOLLOWX_MODE_DEFAULT) { + modePathFromCfg = GetFollowXRule(relPath, &followMode); + if (followMode == FOLLOWX_MODE_USER_DEFINE && modePathFromCfg && *modePathFromCfg) { + extraPath = modePathFromCfg; + } + } + if (extraPath != NULL && strlen(extraPath) > PARAM_CONST_VALUE_LEN_MAX) { + return NULL; + } + switch (followMode) { + case FOLLOWX_MODE_SIM_DEFAULT: + followXPath = GetOpkeyPath(FOLLOWX_MODE_SIM_DEFAULT); + break; + case FOLLOWX_MODE_SIM_1: + followXPath = GetOpkeyPath(FOLLOWX_MODE_SIM_1); + break; + case FOLLOWX_MODE_SIM_2: + followXPath = GetOpkeyPath(FOLLOWX_MODE_SIM_2); + break; + case FOLLOWX_MODE_USER_DEFINE: + followXPath = (extraPath && *extraPath) ? strdup(extraPath) : NULL; + break; + default: + break; + } + char *expandVal = (followXPath && *followXPath) ? ExpandStr(followXPath, "-x-", CustGetSystemParam) : NULL; + FreeIf(followXPath); + FreeIf(modePathFromCfg); + SplitedStr *result = expandVal ? SplitStr(expandVal, ',') : NULL; + return result; +} + +static char *TrimInplace(char *str, bool moveToStart) +{ + char *src = str; + while (isspace(*src)) { + src++; + } + for (size_t i = strlen(src) - 1; i >= 0 && isspace(src[i]); i--) { + src[i] = '\0'; + } + + char *res = moveToStart ? str : src; + if (moveToStart && src != str) { + size_t len = strlen(src) + 1; + if (memmove_s(str, len, src, len) != EOK) { + return NULL; + } + } + return *res ? res : NULL; +} + +static bool EnsureHolderSpace(StringHolder *holder, size_t leastSize) +{ + if (holder->size < leastSize) { + size_t allocSize = Max(leastSize * 2, MIN_APPEND_LEN); + char *newPtr = (char *)calloc(allocSize, sizeof(char)); + if (newPtr == NULL) { + allocSize = leastSize; + newPtr = (char *)calloc(allocSize, sizeof(char)); + if (newPtr == NULL) { + return false; + } + } + if (holder->p != NULL && memcpy_s(newPtr, allocSize, holder->p, holder->strLen) != EOK) { + FreeIf(newPtr); + return false; + } + FreeIf(holder->p); + holder->p = newPtr; + holder->size = allocSize; + } + return true; +} + +static bool AppendStr(StringHolder *holder, const char *s) +{ + size_t leastSize = holder->strLen + strlen(s) + 1; + if (!EnsureHolderSpace(holder, leastSize)) { + return false; + } + if (strcat_s(holder->p, holder->size, s) != EOK) { + return false; + } + holder->strLen = leastSize - 1; + return true; +} + +static char *ExpandStr(char *src, const char *def, QueryFunc queryFunc) +{ + bool ok = true; + StringHolder sh = { 0 }; + char *copyCur = NULL; + char *searchCur = NULL; + char *end = src + strlen(src); + for (copyCur = searchCur = src; ok && searchCur < end;) { + char *varStart = NULL; + char *varEnd = NULL; + char *find = strchr(searchCur, '$'); + if (!find) { + ok = ok && AppendStr(&sh, copyCur); + break; + } else if ((varStart = strchr(find, '{')) && (varStart == find + 1) && (varEnd = strchr(find, '}')) && + varEnd - varStart > 0) { // Get user defined name + *find = *varEnd = 0; + ok = ok && AppendStr(&sh, copyCur); + char *name = find + 2; + char *defVal = strstr(name, SEP_FOR_X_VALUE); + if (defVal) { + *defVal = 0; // cut for name end + defVal = TrimInplace(defVal + strlen(SEP_FOR_X_VALUE), false); // skip ":-", get value + } + char *value = queryFunc(name); + if (value || defVal || def) { + ok = ok && AppendStr(&sh, value ? value : (defVal ? defVal : def)); + FreeIf(value); + } else { + errno = EINVAL; + ok = false; + break; + } + copyCur = searchCur = varEnd + 1; + } else { + searchCur = find + 1; + } + } + if (!ok) { + FreeIf(sh.p); + sh.p = NULL; + } + return sh.p; +} + +static void GetCfgDirRealPolicyValue(CfgDir *res) +{ + if (res == NULL) { + return; + } +#ifndef OHOS_LITE + res->realPolicyValue = CustGetSystemParam(CUST_KEY_POLICY_LAYER); + if (res->realPolicyValue != NULL) { + return; + } +#endif + res->realPolicyValue = strdup("/system:/chipset:/sys_prod:/chip_prod"); +} + void FreeCfgFiles(CfgFiles *res) { if (res == NULL) { @@ -49,26 +381,7 @@ void FreeCfgDirList(CfgDir *res) free(res); } -static void GetCfgDirRealPolicyValue(CfgDir *res) -{ - if (res == NULL) { - return; - } -#ifndef OHOS_LITE - unsigned int len = 0; - (void)SystemGetParameter(CUST_KEY_POLICY_LAYER, NULL, &len); - if (len > 0) { - res->realPolicyValue = (char *)calloc(len, sizeof(char)); - if (res->realPolicyValue != NULL) { - (void)SystemGetParameter(CUST_KEY_POLICY_LAYER, res->realPolicyValue, &len); - return; - } - } -#endif - res->realPolicyValue = strdup("/system:/chipset:/sys_prod:/chip_prod"); -} - -char *GetOneCfgFile(const char *pathSuffix, char *buf, unsigned int bufLength) +char *GetOneCfgFileEx(const char *pathSuffix, char *buf, unsigned int bufLength, int followMode, const char *extra) { if (pathSuffix == NULL || buf == NULL || bufLength < MAX_PATH_LEN) { return NULL; @@ -78,24 +391,42 @@ char *GetOneCfgFile(const char *pathSuffix, char *buf, unsigned int bufLength) if (dirs == NULL) { return NULL; } + + SplitedStr *result = GetFollowXPathByMode(pathSuffix, followMode, extra); for (size_t i = MAX_CFG_POLICY_DIRS_CNT; i > 0; i--) { if (dirs->paths[i - 1] == NULL) { continue; } - if (snprintf_s(buf, bufLength, bufLength - 1, "%s/%s", dirs->paths[i - 1], pathSuffix) == -1) { + // check follow x + for (int j = result ? result->segCount : 0; j > 0; j--) { + if (result->segs[j - 1] && + snprintf_s(buf, bufLength, bufLength - 1, "%s/%s/%s", dirs->paths[i - 1], result->segs[j - 1], + pathSuffix) > 0 && + access(buf, F_OK) == 0) { + break; + } *buf = '\0'; - continue; } - if (access(buf, F_OK) == 0) { + if (*buf) { + break; + } + if (snprintf_s(buf, bufLength, bufLength - 1, "%s/%s", dirs->paths[i - 1], pathSuffix) > 0 && + access(buf, F_OK) == 0) { break; } *buf = '\0'; } FreeCfgDirList(dirs); + FreeSplitedStr(result); return (*buf != '\0') ? buf : NULL; } -CfgFiles *GetCfgFiles(const char *pathSuffix) +char *GetOneCfgFile(const char *pathSuffix, char *buf, unsigned int bufLength) +{ + return GetOneCfgFileEx(pathSuffix, buf, bufLength, FOLLOWX_MODE_DEFAULT, NULL); +} + +CfgFiles *GetCfgFilesEx(const char *pathSuffix, int followMode, const char *extra) { if (pathSuffix == NULL) { return NULL; @@ -110,23 +441,37 @@ CfgFiles *GetCfgFiles(const char *pathSuffix) FreeCfgDirList(dirs); return NULL; } + + SplitedStr *result = GetFollowXPathByMode(pathSuffix, followMode, extra); (void)memset_s(files, sizeof(CfgFiles), 0, sizeof(CfgFiles)); - int j = 0; - for (size_t i = 0; i < MAX_CFG_POLICY_DIRS_CNT; i++) { + int index = 0; + for (size_t i = 0; i < MAX_CFG_POLICY_DIRS_CNT && index < MAX_CFG_POLICY_DIRS_CNT; i++) { if (dirs->paths[i] == NULL) { continue; } - if (snprintf_s(buf, MAX_PATH_LEN, MAX_PATH_LEN - 1, "%s/%s", dirs->paths[i], pathSuffix) == -1) { - continue; + if (snprintf_s(buf, MAX_PATH_LEN, MAX_PATH_LEN - 1, "%s/%s", dirs->paths[i], pathSuffix) > 0 && + access(buf, F_OK) == 0) { + files->paths[index++] = strdup(buf); } - if (access(buf, F_OK) == 0) { - files->paths[j++] = strdup(buf); + for (int j = 0; result && j < result->segCount; j++) { + if (result->segs[j] && + snprintf_s(buf, MAX_PATH_LEN, MAX_PATH_LEN - 1, "%s/%s/%s", dirs->paths[i], result->segs[j], + pathSuffix) > 0 && + access(buf, F_OK) == 0) { + files->paths[index++] = strdup(buf); + } } } FreeCfgDirList(dirs); + FreeSplitedStr(result); return files; } +CfgFiles *GetCfgFiles(const char *pathSuffix) +{ + return GetCfgFilesEx(pathSuffix, FOLLOWX_MODE_DEFAULT, NULL); +} + CfgDir *GetCfgDirList() { CfgDir *res = (CfgDir *)(malloc(sizeof(CfgDir))); diff --git a/interfaces/inner_api/include/config_policy_impl.h b/interfaces/inner_api/include/config_policy_impl.h index 24bc630430d67935f52a4c04b1bf8fa753966609..f695e3bcd1cd0c64c539563895a76320ea4d7bbb 100644 --- a/interfaces/inner_api/include/config_policy_impl.h +++ b/interfaces/inner_api/include/config_policy_impl.h @@ -24,6 +24,11 @@ extern "C" { // these name is used for write data, init may use it #define CUST_KEY_POLICY_LAYER "const.cust.config_dir_layer" +#define CUST_FOLLOW_X_RULES "const.cust.follow_x_rules" +// opkey info for sim1 +#define CUST_OPKEY0 "persist.telephony.opkey0" +// opkey info for sim1 +#define CUST_OPKEY1 "persist.telephony.opkey1" #ifdef __cplusplus #if __cplusplus diff --git a/interfaces/inner_api/include/config_policy_utils.h b/interfaces/inner_api/include/config_policy_utils.h index 9b39afc7b5469fad49fecf8994d8801f4e800047..705da9f7605a30be0d6545274b59063a3403b24d 100644 --- a/interfaces/inner_api/include/config_policy_utils.h +++ b/interfaces/inner_api/include/config_policy_utils.h @@ -22,8 +22,37 @@ extern "C" { #endif #endif // __cplusplus -#define MAX_CFG_POLICY_DIRS_CNT 32 // max number of directories -#define MAX_PATH_LEN 128 // max length of a filepath +#define MAX_CFG_POLICY_DIRS_CNT 32 // max number of directories +#define MAX_PATH_LEN 128 // max length of a filepath + +// follow X(carrier/network/PLMN/...) usage +// 1. etc/cust/followx_file_list.cfg can be in any layer, except follow x dir +// 2. file format: +// 1)line begin with '#' is comment +// 2)every line is a item, item begin with "follow_rule," is a follow rule +// 3)follow rule item has 3~4 segs, every segs split by ',', as below +// follow_rule,relPath,follow_mode,follow_rule_add_path +// >> if follow mode is USER_DEFINE, the next segs is a user defined follow rule +// >> the follow_rule_add_path can contains param variant +// 3. if a relPath has multi follow rules, use the highest priority rule +// LIMIT: the max size of concatenated all follow rule is PARAM_CONST_VALUE_LEN_MAX(4096) +// example: +// follow_rule,etc/xml/config.xml,10 +// follow_rule,etc/xml/config1.xml,100,etc/carrier2/${keyname} +// follow_rule,etc/xml/config2.xml,100,etc/carrier/${keyname:-value} +// Follow rule in followx_file_list.cfg, which stored in param variant +#define FOLLOWX_MODE_DEFAULT 0 +// Not use any follow rule, even exsit followx_file_list.cfg +#define FOLLOWX_MODE_NO_FOLLOW 1 +// Follow rule by default slot +#define FOLLOWX_MODE_SIM_DEFAULT 10 +// Follow rule by slot 1 +#define FOLLOWX_MODE_SIM_1 11 +// Follow rule by slot 2 +#define FOLLOWX_MODE_SIM_2 12 +// User defined follow rule, get follow_rule_add_path from @param extra +// Notice: Follow rule in followx_file_list.cfg will be ignored. +#define FOLLOWX_MODE_USER_DEFINE 100 // Config Files struct CfgFiles { @@ -45,6 +74,14 @@ void FreeCfgFiles(CfgFiles *res); // free struct CfgDir allocated by GetCfgDirList() void FreeCfgDirList(CfgDir *res); +// get the highest priority config file +// pathSuffixStr: the relative path of the config file, e.g. "etc/xml/config.xml" +// buf: recommended buffer length is MAX_PATH_LEN +// followMode: 0/1/10/11/12/100, see FOLLOWX_MODE_* +// extra: User defined follow rule, get follow_rule_add_path +// return: path of the highest priority config file, return '\0' when such a file is not found +char *GetOneCfgFileEx(const char *pathSuffix, char *buf, unsigned int bufLength, int followMode, const char *extra); + // get the highest priority config file // pathSuffixStr: the relative path of the config file, e.g. "etc/xml/config.xml" // buf: recommended buffer length is MAX_PATH_LEN @@ -57,6 +94,14 @@ char *GetOneCfgFile(const char *pathSuffix, char *buf, unsigned int bufLength); // CAUTION: please use FreeCfgFiles() to avoid memory leak. CfgFiles *GetCfgFiles(const char *pathSuffix); +// get config files, ordered by priority from low to high +// pathSuffixStr: the relative path of the config file, e.g. "etc/xml/config.xml" +// followMode: 0/1/10/11/12/100, see FOLLOWX_MODE_* +// extra: User defined follow rule, get follow_rule_add_path +// return: paths of config files +// CAUTION: please use FreeCfgFiles() to avoid memory leak. +CfgFiles *GetCfgFilesEx(const char *pathSuffix, int followMode, const char *extra); + // get config directories, ordered by priority from low to high // return: paths of config directories // CAUTION: please use FreeCfgDirList() to avoid memory leak. diff --git a/test/resource/ohos_test.xml b/test/resource/ohos_test.xml index bef5f403b58e01c96898d6ccea92eae27cc0bcd5..fe0e588df3b76d01eaf83aac8a9627766a3951e4 100644 --- a/test/resource/ohos_test.xml +++ b/test/resource/ohos_test.xml @@ -16,12 +16,24 @@