1/* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15#include <ctype.h> 16#include <errno.h> 17#include <limits.h> 18 19#include "param_manager.h" 20#include "param_trie.h" 21#ifdef SUPPORT_PARAM_LOAD_HOOK 22#include "init_module_engine.h" 23#endif 24#include "securec.h" 25#include "init_cmds.h" 26#include "init_param.h" 27 28/** 29 * Loading system parameter from /proc/cmdline by the following rules: 30 * 1) reserved cmdline with or without ohos.boot. prefix listed in CmdlineIterator 31 will be processed by the specified processor 32 * 2) cmdline not listed in CmdlineIterator but prefixed with ohos.boot will be add by default 33 * 34 * Special cases for sn: 35 * a) if sn value in cmdline is started with "/", it means a file to be read as parameter value 36 * b) if sn or ohos.boot.sn are not specified, try to generate sn by GenerateSnByDefault 37 */ 38#define OHOS_CMDLINE_PARA_PREFIX "ohos.boot." 39#define OHOS_CMDLINE_CONST_PARA_PREFIX "const.product." 40#define OHOS_CMDLINE_PARA_PREFIX_LEN 10 41#define IMPORT_PREFIX_LEN 7 42 43typedef struct CmdLineInfo { 44 const char *name; 45 int (*processor)(const char *name, const char *value); 46} CmdLineInfo; 47 48typedef struct CmdLineInfoContainer { 49 const CmdLineInfo *cmdLineInfo; 50 size_t cmdLineInfoSize; 51} CmdLineInfoContainer; 52 53typedef struct CmdLineIteratorCtx { 54 char *cmdline; 55 bool gotSn; 56 bool *matches; 57} CmdLineIteratorCtx; 58 59static int CommonDealFun(const char *name, const char *value) 60{ 61 int ret = 0; 62 PARAM_LOGV("Add param from cmdline %s %s", name, value); 63 ret = CheckParamName(name, 0); 64 PARAM_CHECK(ret == 0, return ret, "Invalid param name %s", name); 65 PARAM_LOGV("Param name %s, value %s", name, value); 66 ret = WriteParam(name, value, NULL, 0); 67 PARAM_CHECK(ret == 0, return ret, "Failed to write param %s %s", name, value); 68 return ret; 69} 70 71static int ReadSnFromFile(const char *name, const char *file) 72{ 73 char *data = ReadFileData(file); 74 PARAM_CHECK(data != NULL, return -1, "Read sn from %s file failed!", file); 75 76 int index = 0; 77 for (size_t i = 0; i < strlen(data); i++) { 78 // cancel \r\n 79 if (*(data + i) == '\r' || *(data + i) == '\n') { 80 break; 81 } 82 if (*(data + i) != ':') { 83 *(data + index) = *(data + i); 84 index++; 85 } 86 } 87 data[index] = '\0'; 88 PARAM_LOGV("**** name %s, value %s", name, data); 89 int ret = WriteParam(name, data, NULL, 0); 90 free(data); 91 PARAM_CHECK(ret == 0, return ret, "Failed to write param %s", name); 92 return ret; 93} 94 95#define OHOS_SN_PARAM_NAME OHOS_CMDLINE_PARA_PREFIX"sn" 96 97static int SnDealFun(const char *name, const char *value) 98{ 99 int ret = CheckParamName(name, 0); 100 PARAM_CHECK(ret == 0, return ret, "Invalid name %s", name); 101 if (value != NULL && value[0] != '/') { 102 PARAM_LOGV("**** name %s, value %s", name, value); 103 ret = WriteParam(OHOS_SN_PARAM_NAME, value, NULL, 0); 104 PARAM_CHECK(ret == 0, return ret, "Failed to write param %s %s", name, value); 105 return ret; 106 } 107 if (value != NULL && value[0] == '/') { 108 ret = ReadSnFromFile(OHOS_SN_PARAM_NAME, value); 109 if (ret == 0) { 110 return ret; 111 } 112 } 113 return ret; 114} 115 116static int Common2ConstDealFun(const char *name, const char *value) 117{ 118 const char *tmpName; 119 tmpName = name; 120 if (strncmp(tmpName, OHOS_CMDLINE_PARA_PREFIX, OHOS_CMDLINE_PARA_PREFIX_LEN) == 0) { 121 tmpName = tmpName + OHOS_CMDLINE_PARA_PREFIX_LEN; 122 } 123 char fullName[PARAM_NAME_LEN_MAX]; 124 int ret = snprintf_s(fullName, sizeof(fullName), sizeof(fullName) - 1, 125 OHOS_CMDLINE_CONST_PARA_PREFIX"%s", tmpName); 126 PARAM_CHECK(ret > 0, return ret, "snprinf_s failed"); 127 ret = CheckParamName(fullName, 0); 128 PARAM_CHECK(ret == 0, return ret, "Invalid name %s", name); 129 PARAM_LOGV("Param name %s, value %s", fullName, value); 130 ret = WriteParam(fullName, value, NULL, 0); 131 PARAM_CHECK(ret == 0, return ret, "Failed to write param %s %s", fullName, value); 132 return ret; 133} 134 135static int MatchReserverCmdline(const NAME_VALUE_PAIR* nv, CmdLineIteratorCtx *ctx, const char *name, 136 CmdLineInfoContainer *container) 137{ 138 const char* tmpName = name; 139 char fullName[PARAM_NAME_LEN_MAX]; 140 int ret = 0; 141 const char* matched; 142 143 // Matching reserved cmdlines 144 for (size_t i = 0; i < container->cmdLineInfoSize; i++) { 145 // Check exact match 146 if (strcmp(tmpName, (container->cmdLineInfo + i)->name) != 0) { 147 // Check if contains ".xxx" for compatibility 148 ret = snprintf_s(fullName, sizeof(fullName), sizeof(fullName) - 1, ".%s", 149 (container->cmdLineInfo + i)->name); 150 matched = strstr(tmpName, fullName); 151 if (matched == NULL) { 152 continue; 153 } 154 // Check if it is ended with pattern 155 if (matched[ret] != '\0') { 156 continue; 157 } 158 } 159 ret = snprintf_s(fullName, sizeof(fullName), sizeof(fullName) - 1, 160 OHOS_CMDLINE_PARA_PREFIX "%s", (container->cmdLineInfo + i)->name); 161 if (ret <= 0) { 162 continue; 163 } 164 if (ctx->matches[i]) { 165 return PARAM_CODE_SUCCESS; 166 } 167 bool isSnSet = ((container->cmdLineInfo + i)->processor == SnDealFun); 168 if (isSnSet && ctx->gotSn) { 169 return PARAM_CODE_SUCCESS; 170 } 171 PARAM_LOGV("proc cmdline %s matched.", fullName); 172 ret = (container->cmdLineInfo + i)->processor(fullName, nv->value); 173 if (ret == 0) { 174 ctx->matches[i] = true; 175 if (isSnSet) { 176 ctx->gotSn = true; 177 } 178 } 179 return PARAM_CODE_SUCCESS; 180 } 181 return PARAM_CODE_NOT_FOUND; 182} 183 184static const CmdLineInfo CMDLINES[] = { 185 { "hardware", CommonDealFun }, 186 { "bootgroup", CommonDealFun }, 187 { "reboot_reason", CommonDealFun }, 188 { "bootslots", CommonDealFun }, 189 { "sn", SnDealFun }, 190 { "root_package", CommonDealFun }, 191 { "serialno", SnDealFun }, 192 { "udid", Common2ConstDealFun }, 193 { "productid", Common2ConstDealFun } 194}; 195 196static void CmdlineIterator(const NAME_VALUE_PAIR *nv, void *context) 197{ 198 CmdLineIteratorCtx *ctx = (CmdLineIteratorCtx *)context; 199 char *data = (char *)ctx->cmdline; 200 201 data[nv->nameEnd - data] = '\0'; 202 data[nv->valueEnd - data] = '\0'; 203 PARAM_LOGV("proc cmdline: name [%s], value [%s]", nv->name, nv->value); 204 205 // Get name without prefix 206 const char *name = nv->name; 207 if (strncmp(name, OHOS_CMDLINE_PARA_PREFIX, OHOS_CMDLINE_PARA_PREFIX_LEN) == 0) { 208 name = name + OHOS_CMDLINE_PARA_PREFIX_LEN; 209 } 210 211 CmdLineInfoContainer container = { 0 }; 212 container.cmdLineInfo = CMDLINES; 213 container.cmdLineInfoSize = ARRAY_LENGTH(CMDLINES); 214 if (MatchReserverCmdline(nv, ctx, name, &container) == 0) { 215 PARAM_LOGV("match reserver cmd line success, name: %s, value: %s", nv->name, nv->value); 216 return; 217 } 218 if (name == nv->name) { 219 return; 220 } 221 222 // cmdline with prefix but not matched, add to param by default 223 PARAM_LOGI("add proc cmdline param %s by default.", nv->name); 224 CommonDealFun(nv->name, nv->value); 225} 226 227static void GenerateSnByDefault(void) 228{ 229 const char *snFileList [] = { 230 "/sys/block/mmcblk0/device/cid", 231 "/proc/bootdevice/cid" 232 }; 233 234 for (size_t i = 0; i < ARRAY_LENGTH(snFileList); i++) { 235 int ret = ReadSnFromFile(OHOS_CMDLINE_PARA_PREFIX "sn", snFileList[i]); 236 if (ret == 0) { 237 break; 238 } 239 } 240} 241 242INIT_LOCAL_API int LoadParamFromCmdLine(void) 243{ 244 CmdLineIteratorCtx ctx; 245 246 ctx.gotSn = false; 247 ctx.cmdline = ReadFileData(BOOT_CMD_LINE); 248 PARAM_CHECK(ctx.cmdline != NULL, return -1, "Failed to read file %s", BOOT_CMD_LINE); 249 bool matches[ARRAY_LENGTH(CMDLINES)] = {false}; 250 ctx.matches = matches; 251 IterateNameValuePairs(ctx.cmdline, CmdlineIterator, (void *)(&ctx)); 252 253 // sn is critical, it must be specified 254 if (!ctx.gotSn) { 255 PARAM_LOGE("Generate default sn now ..."); 256 GenerateSnByDefault(); 257 } 258 259 free(ctx.cmdline); 260 return 0; 261} 262 263/* 264 * Load parameters from files 265 */ 266 267static int LoadSecurityLabel(const char *fileName) 268{ 269 ParamWorkSpace *paramSpace = GetParamWorkSpace(); 270 PARAM_CHECK(paramSpace != NULL, return -1, "Invalid paramSpace"); 271 PARAM_WORKSPACE_CHECK(paramSpace, return -1, "Invalid space"); 272 PARAM_CHECK(fileName != NULL, return -1, "Invalid filename for load"); 273#if !(defined __LITEOS_A__ || defined __LITEOS_M__) 274 // load security label 275 ParamSecurityOps *ops = GetParamSecurityOps(PARAM_SECURITY_DAC); 276 if (ops != NULL && ops->securityGetLabel != NULL) { 277 if (ops->securityGetLabel(fileName) == PARAM_CODE_REACHED_MAX) { 278 PARAM_LOGE("[startup_failed]Load Security Lable failed! system reboot! %d", SYS_PARAM_INIT_FAILED); 279 ExecReboot("panic"); 280 }; 281 } 282#endif 283 return 0; 284} 285 286static int LoadOneParam_(const uint32_t *context, const char *name, const char *value) 287{ 288 uint32_t mode = *(uint32_t *)context; 289 int ret = CheckParamName(name, 0); 290 if (ret != 0) { 291 return 0; 292 } 293 294#ifdef SUPPORT_PARAM_LOAD_HOOK 295 PARAM_LOAD_FILTER_CTX filter; 296 297 // Filter by hook 298 filter.name = name; 299 filter.value = value; 300 filter.ignored = 0; 301 HookMgrExecute(GetBootStageHookMgr(), INIT_PARAM_LOAD_FILTER, (void *)&filter, NULL); 302 303 if (filter.ignored) { 304 PARAM_LOGV("Default parameter [%s] [%s] ignored", name, value); 305 return 0; 306 } 307#endif 308 309 PARAM_LOGV("Add default parameter [%s] [%s]", name, value); 310 return WriteParam(name, value, NULL, mode & LOAD_PARAM_ONLY_ADD); 311} 312 313static int LoadFileFromImport(char *target, uint32_t mode) 314{ 315 if (strstr(target, ".para.dac")) { 316 LoadSecurityLabel(target); 317 } else { 318 LoadDefaultParams(target, mode); 319 } 320 return 0; 321} 322 323// Content format of .import.para is "import /dir/param.para" 324// Use ${} to pass parameter like "import /dir/${const.product.productid}.para" 325static int LoadParamFromImport_(char *buffer, const int buffSize, uint32_t mode) 326{ 327 int spaceCount = 0; 328 while (*(buffer + IMPORT_PREFIX_LEN + spaceCount) == ' ') { 329 spaceCount++; 330 } 331 char *target = calloc(PATH_MAX, 1); 332 PARAM_CHECK(target != NULL, return -1, "Failed to alloc memory"); 333 if (strncpy_s(target, PATH_MAX, buffer + IMPORT_PREFIX_LEN + spaceCount, buffSize) != 0) { 334 PARAM_LOGE("Failed to get value of import."); 335 free(target); 336 return -1; 337 } 338 char *tmp = NULL; 339 if ((tmp = strstr(target, "\n"))) { 340 *tmp = '\0'; 341 } 342 char *tmpParamValue = calloc(PARAM_VALUE_LEN_MAX + 1, sizeof(char)); 343 if (tmpParamValue == NULL) { 344 PARAM_LOGE("Failed to alloc memory"); 345 free(target); 346 return -1; 347 } 348 int ret = GetParamValue(target, strlen(target), tmpParamValue, PARAM_VALUE_LEN_MAX); 349 if (ret == 0) { 350 LoadFileFromImport(tmpParamValue, mode); 351 } 352 PARAM_LOGI("Load params from import %s return %d.", tmpParamValue, ret); 353 free(tmpParamValue); 354 free(target); 355 return ret; 356} 357 358static int LoadParamFromImport(const char *fileName, void *context) 359{ 360 char realPath[PATH_MAX] = ""; 361 realpath(fileName, realPath); 362 FILE *fp = fopen(realPath, "r"); 363 if (fp == NULL) { 364 PARAM_LOGE("Failed to open file '%s' error:%d ", fileName, errno); 365 return -1; 366 } 367 368 const int buffSize = PATH_MAX; 369 char *buffer = calloc(buffSize, sizeof(char)); 370 PARAM_CHECK(buffer != NULL, (void)fclose(fp); 371 return -1, "Failed to alloc memory"); 372 373 uint32_t mode = *(int *)context; 374 while (fgets(buffer, buffSize, fp) != NULL) { 375 buffer[buffSize - 1] = '\0'; 376 if (!strncmp(buffer, "import ", IMPORT_PREFIX_LEN)) { 377 (void)LoadParamFromImport_(buffer, buffSize, mode); 378 } 379 } 380 (void)fclose(fp); 381 free(buffer); 382 return 0; 383} 384 385static int LoadDefaultParam_(const char *fileName, uint32_t mode, 386 const char *exclude[], uint32_t count, int (*loadOneParam)(const uint32_t *, const char *, const char *)) 387{ 388 uint32_t paramNum = 0; 389 char realPath[PATH_MAX] = ""; 390 realpath(fileName, realPath); 391 FILE *fp = fopen(realPath, "r"); 392 if (fp == NULL) { 393 PARAM_LOGW("Failed to open file '%s' error:%d ", fileName, errno); 394 return -1; 395 } 396 397 const int buffSize = PARAM_NAME_LEN_MAX + PARAM_CONST_VALUE_LEN_MAX + 10; // 10 max len 398 char *buffer = calloc(buffSize, sizeof(char)); 399 PARAM_CHECK(buffer != NULL, (void)fclose(fp); 400 return -1, "Failed to alloc memory"); 401 402 while (fgets(buffer, buffSize, fp) != NULL) { 403 buffer[buffSize - 1] = '\0'; 404 int ret = SplitParamString(buffer, exclude, count, loadOneParam, &mode); 405 PARAM_ONLY_CHECK(ret != PARAM_DEFAULT_PARAM_MEMORY_NOT_ENOUGH, 406 (void)fclose(fp); free(buffer); return PARAM_DEFAULT_PARAM_MEMORY_NOT_ENOUGH); 407 if (mode == LOAD_PARAM_ONLY_ADD && ret == PARAM_CODE_READ_ONLY) { 408 PARAM_WARNING_CHECK(ret == 0, continue, "Set param '%s' error:%d with only add mode", buffer, ret); 409 } else { 410 PARAM_CHECK(ret == 0, continue, "Failed to set param '%s' error:%d ", buffer, ret); 411 } 412 paramNum++; 413 } 414 (void)fclose(fp); 415 free(buffer); 416 PARAM_LOGV("Load %u default parameters success from %s.", paramNum, fileName); 417 return 0; 418} 419 420static int ProcessParamFile(const char *fileName, void *context) 421{ 422 static const char *exclude[] = {"ctl.", "selinux.restorecon_recursive"}; 423 uint32_t mode = *(int *)context; 424 int ret = LoadDefaultParam_(fileName, mode, exclude, ARRAY_LENGTH(exclude), LoadOneParam_); 425 if (ret == PARAM_DEFAULT_PARAM_MEMORY_NOT_ENOUGH) { 426 PARAM_LOGE("[startup_failed]default_param memory is not enough, system reboot! %d", SYS_PARAM_INIT_FAILED); 427 ExecReboot("panic"); 428 } 429 return ret; 430} 431 432int LoadParamsFile(const char *fileName, bool onlyAdd) 433{ 434 return LoadDefaultParams(fileName, onlyAdd ? LOAD_PARAM_ONLY_ADD : LOAD_PARAM_NORMAL); 435} 436 437int LoadDefaultParams(const char *fileName, uint32_t mode) 438{ 439 PARAM_CHECK(fileName != NULL, return -1, "Invalid filename for load"); 440 PARAM_LOGI("Load default parameters from %s.", fileName); 441 struct stat st; 442 if ((stat(fileName, &st) == 0) && !S_ISDIR(st.st_mode)) { 443 if (strstr(fileName, ".para.dac")) { 444 return LoadSecurityLabel(fileName); 445 } else { 446 return ProcessParamFile(fileName, &mode); 447 } 448 } else { 449 (void)ReadFileInDir(fileName, ".para", ProcessParamFile, &mode); 450 (void)ReadFileInDir(fileName, ".para.import", LoadParamFromImport, &mode); 451 return LoadSecurityLabel(fileName); 452 } 453} 454 455INIT_LOCAL_API void LoadParamFromBuild(void) 456{ 457 PARAM_LOGI("load parameters from build "); 458#ifdef INCREMENTAL_VERSION 459 if (strlen(INCREMENTAL_VERSION) > 0) { 460 WriteParam("const.product.incremental.version", INCREMENTAL_VERSION, NULL, LOAD_PARAM_NORMAL); 461 } 462#endif 463#ifdef BUILD_TYPE 464 if (strlen(BUILD_TYPE) > 0) { 465 WriteParam("const.product.build.type", BUILD_TYPE, NULL, LOAD_PARAM_NORMAL); 466 } 467#endif 468#ifdef BUILD_USER 469 if (strlen(BUILD_USER) > 0) { 470 WriteParam("const.product.build.user", BUILD_USER, NULL, LOAD_PARAM_NORMAL); 471 } 472#endif 473#ifdef BUILD_TIME 474 if (strlen(BUILD_TIME) > 0) { 475 WriteParam("const.product.build.date", BUILD_TIME, NULL, LOAD_PARAM_NORMAL); 476 } 477#endif 478#ifdef BUILD_HOST 479 if (strlen(BUILD_HOST) > 0) { 480 WriteParam("const.product.build.host", BUILD_HOST, NULL, LOAD_PARAM_NORMAL); 481 } 482#endif 483#ifdef BUILD_ROOTHASH 484 if (strlen(BUILD_ROOTHASH) > 0) { 485 WriteParam("const.ohos.buildroothash", BUILD_ROOTHASH, NULL, LOAD_PARAM_NORMAL); 486 } 487#endif 488} 489 490static int LoadOneParamAreaSize_(const uint32_t *context, const char *name, const char *value) 491{ 492 uint32_t size = (uint32_t)strtoul(value, NULL, DECIMAL_BASE); 493 PARAM_LOGV("LoadOneParamAreaSize_ [%s] [%s]", name, value); 494 ParamWorkSpace *paramSpace = GetParamWorkSpace(); 495 PARAM_CHECK(paramSpace != NULL && paramSpace->workSpace != NULL, 496 return -1, "Invalid workspace name %s", name); 497 WorkSpaceSize *spaceSize = GetWorkSpaceSize(GetWorkSpace(WORKSPACE_INDEX_SIZE)); 498 PARAM_CHECK(spaceSize != NULL, return PARAM_CODE_ERROR, "Failed to get workspace size"); 499 static char buffer[SELINUX_CONTENT_LEN] = {0}; 500 int ret = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "u:object_r:%s:s0", name); 501 PARAM_CHECK(ret > 0, return PARAM_CODE_ERROR, "Failed to snprintf workspace name"); 502 503 for (uint32_t i = WORKSPACE_INDEX_BASE + 1; i < spaceSize->maxLabelIndex; i++) { 504 if (paramSpace->workSpace[i] == NULL) { 505 continue; 506 } 507 if (strcmp(paramSpace->workSpace[i]->fileName, buffer) == 0) { 508 spaceSize->spaceSize[i] = size; 509 paramSpace->workSpace[i]->spaceSize = size; 510 break; 511 } 512 } 513 return 0; 514} 515 516INIT_LOCAL_API void LoadParamAreaSize(void) 517{ 518 LoadDefaultParam_("/sys_prod/etc/param/ohos.para.size", 0, NULL, 0, LoadOneParamAreaSize_); 519 LoadDefaultParam_(PARAM_AREA_SIZE_CFG, 0, NULL, 0, LoadOneParamAreaSize_); 520} 521