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