1/*
2 * Copyright (c) 2020 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 <gtest/gtest.h>
16#include <cerrno>
17#include <cstdio>
18#include <cstdlib>
19#include <dirent.h>
20#include <string>
21#include <sys/stat.h>
22#include <sys/types.h>
23
24#include "cJSON.h"
25#include "init_cmds.h"
26#include "init_jobs_internal.h"
27#include "init_log.h"
28#include "init_service_manager.h"
29#include "param_stub.h"
30#include "securec.h"
31
32using namespace std;
33using namespace testing::ext;
34
35namespace OHOS {
36std::vector<std::string> g_supportedCmds;
37const std::string ROOT_DIR = "/storage/data/";
38const std::string TEST_DRI = ROOT_DIR + "StartInitTestDir";
39const std::string TEST_FILE = TEST_DRI + "/test.txt";
40const std::string TEST_CFG_ILLEGAL = TEST_DRI + "/illegal.cfg";
41const std::string TEST_PROC_MOUNTS = "/proc/mounts";
42#ifndef USE_EMMC_STORAGE
43const uid_t TEST_FILE_UID = 999;
44const gid_t TEST_FILE_GID = 999;
45const mode_t TEST_FILE_MODE = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
46#endif
47
48// init.cfg related
49const std::string CFG_FILE = "/etc/init.cfg";
50const std::string SERVICE_ARR_NAME_IN_JSON = "services";
51const std::string JOBS_ARR_NAME_IN_JSON = "jobs";
52const std::string CMDS_ARR_NAME_IN_JSON = "cmds";
53const uid_t CFG_FILE_UID = 0;
54const gid_t CFG_FILE_GID = 0;
55const mode_t CFG_FILE_MODE = S_IRUSR;
56const int JOBS_IN_FILE_COUNT = 3; // pre-init, init, post-init
57const int MAX_SERVICES_COUNT_IN_FILE = 100;
58const int MAX_CAPS_CNT_FOR_ONE_SERVICE = 100;
59const unsigned int MAX_JSON_FILE_LEN = 102400;        // max init.cfg size 100KB
60const int TEST_MAX_PATH_ARGS_CNT = 20;                // max path and args count
61const int TEST_MAX_ONE_ARG_LEN = 64;                  // max length of one param/path
62const int CAT_BUF_SIZE = 512;                         // standard Cat buffer size from vfs_shell_cmd
63
64// job test related
65const std::string PRE_INIT_DIR = ROOT_DIR + "preInitDir/";
66const std::string INIT_DIR = PRE_INIT_DIR + "initDir";
67const std::string POST_INIT_DIR = INIT_DIR + "postInitDir";
68
69using  TestCmdLine = struct {
70    char name[MAX_CMD_NAME_LEN + 1];
71    char cmdContent[MAX_CMD_CONTENT_LEN + 1];
72};
73
74class StartupInitUTest : public testing::Test {
75public:
76    static void SetUpTestCase()
77    {
78        g_supportedCmds.push_back(std::string("start "));
79        g_supportedCmds.push_back(std::string("mkdir "));
80        g_supportedCmds.push_back(std::string("chmod "));
81        g_supportedCmds.push_back(std::string("chown "));
82        g_supportedCmds.push_back(std::string("mount "));
83        g_supportedCmds.push_back(std::string("loadcfg "));
84        const mode_t mode = 0755;
85        if (mkdir(TEST_DRI.c_str(), mode) != 0) {
86            if (errno != EEXIST) {
87                return;
88            }
89        }
90
91        FILE *testFile = fopen(TEST_FILE.c_str(), "w+");
92        if (testFile == nullptr) {
93            return;
94        }
95
96        std::string writeContent = "This is a test file for startup subsystem init module.";
97        if (fwrite(writeContent.c_str(), writeContent.length(), 1, testFile) != 1) {
98            (void)fclose(testFile);
99            return;
100        }
101        (void)fclose(testFile);
102
103#ifndef USE_EMMC_STORAGE    // emmc storage does not support chmod/chown
104
105        if (chmod(TEST_FILE.c_str(), TEST_FILE_MODE) != 0) {
106            return;
107        }
108
109        if (chown(TEST_FILE.c_str(), TEST_FILE_UID, TEST_FILE_GID) != 0) {
110            return;
111        }
112#endif  // USE_EMMC_STORAGE
113        PrepareInitUnitTestEnv();
114    }
115
116    static void TearDownTestCase()
117    {
118        if (remove(TEST_FILE.c_str()) != 0) {
119            return;
120        }
121        if (remove(TEST_DRI.c_str()) != 0) {
122            return;
123        }
124    }
125    void SetUp()
126    {
127        EnableInitLog(INIT_FATAL);
128    }
129    void TearDown() {}
130};
131
132void ParseCmdLine(const char *content, TestCmdLine *resCmd)
133{
134    if (content == nullptr || resCmd == nullptr) {
135        return;
136    }
137
138    const struct CmdTable *cmd = GetCmdByName(content);
139    if (cmd == nullptr) {
140        (void)memset_s(resCmd, sizeof(TestCmdLine), 0, sizeof(TestCmdLine));
141        return;
142    }
143    if (strlen(content) <= (strlen(cmd->name) + 1)) {
144        (void)memset_s(resCmd, sizeof(TestCmdLine), 0, sizeof(TestCmdLine));
145        return;
146    }
147    int ret1 = strcpy_s(resCmd->name, MAX_CMD_NAME_LEN, cmd->name);
148    int ret2 = strcpy_s(resCmd->cmdContent, MAX_CMD_CONTENT_LEN, content + strlen(cmd->name));
149    if (ret1 || ret2) {
150        (void)memset_s(resCmd, sizeof(TestCmdLine), 0, sizeof(TestCmdLine));
151        return;
152    }
153}
154
155void DoCmd(const TestCmdLine *resCmd)
156{
157    if (resCmd == nullptr) {
158        return;
159    }
160    int cmdIndex = 0;
161    (void)GetMatchCmd(resCmd->name, &cmdIndex);
162    DoCmdByIndex(cmdIndex, resCmd->cmdContent, nullptr);
163}
164
165/*
166 * @tc.name: cmdFuncParseCmdTest_001
167 * @tc.desc: parse function, nullptr test
168 * @tc.type: FUNC
169 */
170HWTEST_F(StartupInitUTest, cmdFuncParseCmdTest_001, TestSize.Level0)
171{
172    // do not crash
173    ParseCmdLine(nullptr, nullptr);
174};
175
176/*
177 * @tc.name: cmdFuncParseCmdTest_002
178 * @tc.desc: parse function, invalid strings test
179 * @tc.type: FUNC
180 */
181HWTEST_F(StartupInitUTest, cmdFuncParseCmdTest_002, TestSize.Level0)
182{
183    TestCmdLine curCmdLine;
184    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
185
186    ParseCmdLine(nullptr, &curCmdLine);
187    EXPECT_EQ(0, strlen(curCmdLine.name));
188    EXPECT_EQ(0, strlen(curCmdLine.cmdContent));
189
190    ParseCmdLine("", &curCmdLine);
191    EXPECT_EQ(0, strlen(curCmdLine.name));
192    EXPECT_EQ(0, strlen(curCmdLine.cmdContent));
193
194    ParseCmdLine("xxxxxxxx", &curCmdLine);
195    EXPECT_EQ(0, strlen(curCmdLine.name));
196    EXPECT_EQ(0, strlen(curCmdLine.cmdContent));
197
198    ParseCmdLine("asdnkawdqw4145a45sdqw_-+\\\\sdqwdasd", &curCmdLine);
199    EXPECT_EQ(0, strlen(curCmdLine.name));
200    EXPECT_EQ(0, strlen(curCmdLine.cmdContent));
201}
202
203/*
204 * @tc.name: cmdFuncParseCmdTest_003
205 * @tc.desc: parse function, cmd content empty test
206 * @tc.type: FUNC
207 */
208HWTEST_F(StartupInitUTest, cmdFuncParseCmdTest_003, TestSize.Level0)
209{
210    TestCmdLine curCmdLine;
211    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
212
213    for (size_t i = 0; i < g_supportedCmds.size(); ++i) {
214        ParseCmdLine(g_supportedCmds[i].c_str(), &curCmdLine);
215        EXPECT_EQ(0, strlen(curCmdLine.name));
216        EXPECT_EQ(0, strlen(curCmdLine.cmdContent));
217    }
218}
219
220/*
221 * @tc.name: cmdFuncParseCmdTest_004
222 * @tc.desc: parse function, cmd content too long test
223 * @tc.type: FUNC
224 */
225HWTEST_F(StartupInitUTest, cmdFuncParseCmdTest_004, TestSize.Level0)
226{
227    TestCmdLine curCmdLine;
228    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
229
230    char toLongContent[MAX_CMD_CONTENT_LEN + 10];
231    int ret = memset_s(toLongContent, MAX_CMD_CONTENT_LEN + 10, 'x', MAX_CMD_CONTENT_LEN + 9);
232    EXPECT_EQ(0, ret);
233
234    toLongContent[MAX_CMD_CONTENT_LEN + 9] = '\0';
235    for (size_t i = 0; i < g_supportedCmds.size(); ++i) {
236        size_t curCmdLen = g_supportedCmds[i].length();
237        char *curCmd = (char *)malloc(curCmdLen + MAX_CMD_CONTENT_LEN + 10);
238        if (curCmd == nullptr) {
239            break;
240        }
241        errno_t ret = memcpy_s(curCmd, curCmdLen + MAX_CMD_CONTENT_LEN + 10, \
242            g_supportedCmds[i].c_str(), curCmdLen);
243        errno_t ret2 = memcpy_s(curCmd + curCmdLen, MAX_CMD_CONTENT_LEN + 10, \
244            toLongContent, strlen(toLongContent));
245        if (ret != EOK || ret2 != EOK) {
246            free(curCmd);
247            curCmd = nullptr;
248            break;
249        }
250        curCmd[curCmdLen + MAX_CMD_CONTENT_LEN + 9] = '\0';
251
252        ParseCmdLine(curCmd, &curCmdLine);
253        EXPECT_EQ(0, strlen(curCmdLine.name));
254        EXPECT_EQ(0, strlen(curCmdLine.cmdContent));
255        free(curCmd);
256        curCmd = nullptr;
257    }
258}
259
260/*
261 * @tc.name: cmdFuncParseCmdTest_005
262 * @tc.desc: parse function, parse success test
263 * @tc.type: FUNC
264 */
265HWTEST_F(StartupInitUTest, cmdFuncParseCmdTest_005, TestSize.Level0)
266{
267    TestCmdLine curCmdLine;
268    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
269
270    ParseCmdLine("start InitTestService", &curCmdLine);
271    EXPECT_EQ(0, strcmp("start ", curCmdLine.name));
272    EXPECT_EQ(0, strcmp("InitTestService", curCmdLine.cmdContent));
273
274    ParseCmdLine("mkdir InitTestDir", &curCmdLine);
275    EXPECT_EQ(0, strcmp("mkdir ", curCmdLine.name));
276    EXPECT_EQ(0, strcmp("InitTestDir", curCmdLine.cmdContent));
277
278    ParseCmdLine("chmod 0500 /bin/InitTestBin", &curCmdLine);
279    EXPECT_EQ(0, strcmp("chmod ", curCmdLine.name));
280    EXPECT_EQ(0, strcmp("0500 /bin/InitTestBin", curCmdLine.cmdContent));
281
282    ParseCmdLine("chown 1000 1000 /bin/InitTestBin", &curCmdLine);
283    EXPECT_EQ(0, strcmp("chown ", curCmdLine.name));
284    EXPECT_EQ(0, strcmp("1000 1000 /bin/InitTestBin", curCmdLine.cmdContent));
285
286    ParseCmdLine("mount vfat /dev/mmcblk1 /sdcard rw,umask=000", &curCmdLine);
287    EXPECT_EQ(0, strcmp("mount ", curCmdLine.name));
288    EXPECT_EQ(0, strcmp("vfat /dev/mmcblk1 /sdcard rw,umask=000", curCmdLine.cmdContent));
289};
290
291/*
292 * @tc.name: cmdFuncDoCmdTest_001
293 * @tc.desc: do cmd function, nullptr test
294 * @tc.type: FUNC
295 */
296HWTEST_F(StartupInitUTest, cmdFuncDoCmdTest_001, TestSize.Level0)
297{
298    // do not crash here
299    DoCmd(nullptr);
300}
301
302/*
303 * @tc.name: cmdFuncDoCmdTest_002
304 * @tc.desc: do cmd function, do start fail test
305 * @tc.type: FUNC
306 */
307HWTEST_F(StartupInitUTest, cmdFuncDoCmdTest_002, TestSize.Level0)
308{
309    TestCmdLine curCmdLine;
310    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
311
312    std::string cmdStr = "start ";
313    std::string cmdContentStr = "NameNotExist";
314    std::string command = cmdStr + cmdContentStr;
315    ParseCmdLine(command.c_str(), &curCmdLine);
316    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
317    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
318    DoCmd(&curCmdLine);
319}
320
321/*
322 * @tc.name: cmdFuncDoCmdTest_003
323 * @tc.desc: do cmd function, do mkdir fail test
324 * @tc.type: FUNC
325 */
326HWTEST_F(StartupInitUTest, cmdFuncDoCmdTest_003, TestSize.Level0)
327{
328    TestCmdLine curCmdLine;
329    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
330
331    std::string cmdStr = "mkdir ";
332    std::string cmdContentStr = "/DirNotExist/DirNotExist/DirNotExist";
333    std::string command = cmdStr + cmdContentStr;
334    ParseCmdLine(command.c_str(), &curCmdLine);
335    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
336    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
337    DoCmd(&curCmdLine);
338
339    // make sure that the directory does not exist
340    DIR *dirTmp = opendir(cmdContentStr.c_str());
341    EXPECT_TRUE(dirTmp == nullptr);
342    EXPECT_TRUE(errno == ENOENT);
343    if (dirTmp != nullptr) {    // just in case
344        closedir(dirTmp);
345        dirTmp = nullptr;
346    }
347
348    // error argument count, bad format
349    cmdContentStr = "  /storage/data/cmdFuncDoCmdTest003 0755 system";
350    command = cmdStr + cmdContentStr;
351    ParseCmdLine(command.c_str(), &curCmdLine);
352    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
353    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
354    DoCmd(&curCmdLine);
355
356    // make sure that the directory does not exist
357    dirTmp = opendir("/storage/data/cmdFuncDoCmdTest003");
358    EXPECT_TRUE(dirTmp == nullptr);
359    EXPECT_TRUE(errno == ENOENT);
360    if (dirTmp != nullptr) {    // just in case
361        closedir(dirTmp);
362        dirTmp = nullptr;
363    }
364}
365
366/*
367 * @tc.name: cmdFuncDoCmdTest_004
368 * @tc.desc: do cmd function, do chmod fail test
369 * @tc.type: FUNC
370 */
371HWTEST_F(StartupInitUTest, cmdFuncDoCmdTest_004, TestSize.Level0)
372{
373    TestCmdLine curCmdLine;
374    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
375
376    std::string cmdStr = "chmod ";
377    std::string cmdContentStr = "755 " + TEST_FILE;    // should be 0755, wrong format here
378    std::string command = cmdStr + cmdContentStr;
379    ParseCmdLine(command.c_str(), &curCmdLine);
380    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
381    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
382    DoCmd(&curCmdLine);
383
384    cmdContentStr = "0855 " + TEST_FILE;    // should not exceed 0777, wrong format here
385    command = cmdStr + cmdContentStr;
386    ParseCmdLine(command .c_str(), &curCmdLine);
387    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
388    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
389    DoCmd(&curCmdLine);
390
391    cmdContentStr = "07b5 " + TEST_FILE;    // non-digital character, wrong format here
392    command = cmdStr + cmdContentStr;
393    ParseCmdLine(command.c_str(), &curCmdLine);
394    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
395    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
396    DoCmd(&curCmdLine);
397
398    cmdContentStr = "075 " + TEST_FILE;    // should be 0xxx, wrong format here
399    command = cmdStr + cmdContentStr;
400    ParseCmdLine(command.c_str(), &curCmdLine);
401    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
402    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
403    DoCmd(&curCmdLine);
404
405    cmdContentStr = "0755       " + TEST_FILE;    // too many spaces, wrong format here
406    command = cmdStr + cmdContentStr;
407    ParseCmdLine(command.c_str(), &curCmdLine);
408    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
409    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
410    DoCmd(&curCmdLine);
411
412    struct stat testFileStat = {0};
413    EXPECT_EQ(0, stat(TEST_FILE.c_str(), &testFileStat));
414
415#ifndef USE_EMMC_STORAGE    // emmc storage does not support chmod/chown
416
417    EXPECT_EQ(TEST_FILE_MODE, testFileStat.st_mode & TEST_FILE_MODE);   // file mode is not changed
418
419#endif // USE_EMMC_STORAGE
420}
421
422/*
423 * @tc.name: cmdFuncDoCmdTest_005
424 * @tc.desc: do cmd function, do chown fail test
425 * @tc.type: FUNC
426 */
427HWTEST_F(StartupInitUTest, cmdFuncDoCmdTest_005, TestSize.Level0)
428{
429    TestCmdLine curCmdLine;
430    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
431
432    std::string cmdStr = "chown ";
433    std::string cmdContentStr = "888 " + TEST_FILE;    // uid or gid missing, wrong format here
434    std::string command = cmdStr + cmdContentStr;
435    ParseCmdLine(command.c_str(), &curCmdLine);
436    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
437    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
438    DoCmd(&curCmdLine);
439
440    cmdContentStr = "888 8b9 " + TEST_FILE;    // non-digital character, wrong format here
441    command = cmdStr + cmdContentStr;
442    ParseCmdLine(command.c_str(), &curCmdLine);
443    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
444    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
445    DoCmd(&curCmdLine);
446
447    struct stat testFileStat = {0};
448    EXPECT_EQ(0, stat(TEST_FILE.c_str(), &testFileStat));
449
450#ifndef USE_EMMC_STORAGE    // emmc storage does not support chmod/chown
451
452    EXPECT_EQ(testFileStat.st_uid, TEST_FILE_UID);    // uid not changed
453    EXPECT_EQ(testFileStat.st_gid, TEST_FILE_GID);    // gid not changed
454
455#endif // USE_EMMC_STORAGE
456}
457
458/*
459 * @tc.name: cmdFuncDoCmdTest_006
460 * @tc.desc: do cmd function, do success test
461 * @tc.type: FUNC
462 */
463HWTEST_F(StartupInitUTest, cmdFuncDoCmdTest_006, TestSize.Level0)
464{
465    TestCmdLine curCmdLine;
466
467    // mkdir success
468    std::string cmdStr = "mkdir ";
469    std::string cmdContentStr = TEST_DRI + "/cmdFuncDoCmdTest006";
470    std::string command = cmdStr + cmdContentStr;
471    ParseCmdLine(command.c_str(), &curCmdLine);
472    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
473    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
474
475    DoCmd(&curCmdLine);
476    DIR* dirTmp = opendir(cmdContentStr.c_str());
477    EXPECT_TRUE(dirTmp != nullptr);
478    if (dirTmp != nullptr) {
479        closedir(dirTmp);
480        dirTmp = nullptr;
481    }
482
483    remove(cmdContentStr.c_str());
484    // chmod success
485    cmdStr = "chmod ";
486    cmdContentStr = "0440 " + TEST_FILE;
487    command = cmdStr + cmdContentStr;
488    ParseCmdLine(command.c_str(), &curCmdLine);
489    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
490    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
491
492#ifndef USE_EMMC_STORAGE    // emmc storage does not support chmod/chown
493
494    DoCmd(&curCmdLine);
495    struct stat testFileStat = {0};
496    EXPECT_EQ(0, stat(TEST_FILE.c_str(), &testFileStat));
497    mode_t targetMode = S_IRUSR | S_IRGRP;
498    EXPECT_EQ(targetMode, testFileStat.st_mode & targetMode);    // changed
499
500#endif  // USE_EMMC_STORAGE
501
502    // chown success
503    cmdStr = "chown ";
504    cmdContentStr = "888 888 " + TEST_FILE;
505    command = cmdStr + cmdContentStr;
506    ParseCmdLine(command.c_str(), &curCmdLine);
507    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
508    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
509
510#ifndef USE_EMMC_STORAGE    // emmc storage does not support chmod/chown
511
512    DoCmd(&curCmdLine);
513    EXPECT_EQ(0, stat(TEST_FILE.c_str(), &testFileStat));
514    EXPECT_EQ(testFileStat.st_uid, 888);    // changed
515    EXPECT_EQ(testFileStat.st_gid, 888);    // changed
516
517#endif  // USE_EMMC_STORAGE
518}
519
520/*
521 * @tc.name: cfgCheckStat_001
522 * @tc.desc: init.cfg file state check
523 * @tc.type: FUNC
524 */
525HWTEST_F(StartupInitUTest, cfgCheckStat_001, TestSize.Level0)
526{
527    struct stat fileStat = {0};
528    EXPECT_EQ(0, stat(CFG_FILE.c_str(), &fileStat));
529    EXPECT_EQ(CFG_FILE_UID, fileStat.st_uid);
530    EXPECT_EQ(CFG_FILE_GID, fileStat.st_gid);
531    EXPECT_EQ(CFG_FILE_MODE, CFG_FILE_MODE & fileStat.st_mode);
532    EXPECT_TRUE(fileStat.st_size > 0);
533    EXPECT_TRUE(fileStat.st_size <= MAX_JSON_FILE_LEN);
534};
535
536static char* ReadFileToBuf()
537{
538    char *buffer = nullptr;
539    FILE *fd = nullptr;
540    struct stat fileStat = {0};
541    (void)stat(CFG_FILE.c_str(), &fileStat);
542    do {
543        fd = fopen(CFG_FILE.c_str(), "r");
544        if (fd == nullptr) {
545            break;
546        }
547
548        buffer = static_cast<char*>(malloc(static_cast<size_t>(fileStat.st_size) + 1));
549        if (buffer == nullptr) {
550            break;
551        }
552
553        if (fread(buffer, fileStat.st_size, 1, fd) != 1) {
554            free(buffer);
555            buffer = nullptr;
556            break;
557        }
558        buffer[fileStat.st_size] = '\0';
559    } while (0);
560
561    if (fd != nullptr) {
562        fclose(fd);
563        fd = nullptr;
564    }
565    return buffer;
566}
567
568static cJSON *GetArrItem(const cJSON *fileRoot, int &arrSize, const std::string &arrName)
569{
570    cJSON *arrItem = cJSON_GetObjectItemCaseSensitive(fileRoot, arrName.c_str());
571    arrSize = cJSON_GetArraySize(arrItem);
572    if (arrSize <= 0) {
573        return nullptr;
574    }
575    return arrItem;
576}
577
578static int IsForbidden(const char *fieldStr)
579{
580    size_t fieldLen = strlen(fieldStr);
581    size_t forbidStrLen =  strlen("/bin/sh");
582    if (fieldLen == forbidStrLen) {
583        if (strncmp(fieldStr, "/bin/sh", fieldLen) == 0) {
584            return 1;
585        }
586        return 0;
587    } else if (fieldLen > forbidStrLen) {
588        // "/bin/shxxxx" is valid but "/bin/sh xxxx" is invalid
589        if (strncmp(fieldStr, "/bin/sh", forbidStrLen) == 0) {
590            if (fieldStr[forbidStrLen] == ' ') {
591                return 1;
592            }
593        }
594        return 0;
595    } else {
596        return 0;
597    }
598}
599
600static void CheckService(const cJSON* curItem)
601{
602    if (curItem == nullptr) {
603        return;
604    }
605
606    char *nameStr = cJSON_GetStringValue(cJSON_GetObjectItem(curItem, "name"));
607    if (nameStr == nullptr) {
608        EXPECT_TRUE(nameStr != nullptr);
609    } else {
610        EXPECT_TRUE(strlen(nameStr) > 0);
611    }
612
613    cJSON *pathArgsItem = cJSON_GetObjectItem(curItem, "path");
614    EXPECT_TRUE(cJSON_IsArray(pathArgsItem));
615
616    int pathArgsCnt = cJSON_GetArraySize(pathArgsItem);
617    EXPECT_TRUE(pathArgsCnt > 0);
618    EXPECT_TRUE(pathArgsCnt <= TEST_MAX_PATH_ARGS_CNT);
619
620    for (int i = 0; i < pathArgsCnt; ++i) {
621        char *curParam = cJSON_GetStringValue(cJSON_GetArrayItem(pathArgsItem, i));
622        EXPECT_TRUE(curParam != nullptr);
623        EXPECT_TRUE(strlen(curParam) > 0);
624        EXPECT_TRUE(strlen(curParam) <= TEST_MAX_ONE_ARG_LEN);
625        if (i == 0) {
626            EXPECT_TRUE(IsForbidden(curParam) == 0);
627        }
628    }
629
630    cJSON *filedJ = cJSON_GetObjectItem(curItem, "uid");
631    EXPECT_TRUE(cJSON_IsNumber(filedJ) || cJSON_IsString(filedJ));
632    EXPECT_TRUE(cJSON_GetNumberValue(filedJ) >= 0.0 || cJSON_GetStringValue(filedJ));
633
634    filedJ = cJSON_GetObjectItem(curItem, "gid");
635    EXPECT_TRUE(cJSON_IsNumber(filedJ) || cJSON_IsArray(filedJ));
636    EXPECT_TRUE(cJSON_GetNumberValue(filedJ) >= 0.0 || cJSON_GetArraySize(filedJ) >= 0);
637
638    filedJ = cJSON_GetObjectItem(curItem, "once");
639    EXPECT_TRUE(cJSON_IsNumber(filedJ));
640
641    filedJ = cJSON_GetObjectItem(curItem, "importance");
642    EXPECT_TRUE(cJSON_IsNumber(filedJ));
643
644    filedJ = cJSON_GetObjectItem(curItem, "caps");
645    EXPECT_TRUE(cJSON_IsArray(filedJ));
646    int capsCnt = cJSON_GetArraySize(filedJ);
647    EXPECT_TRUE(capsCnt <= MAX_CAPS_CNT_FOR_ONE_SERVICE);
648    for (int i = 0; i < capsCnt; ++i) {
649        cJSON *capJ = cJSON_GetArrayItem(filedJ, i);
650        EXPECT_TRUE(cJSON_IsNumber(capJ) || cJSON_GetStringValue(capJ));
651        EXPECT_TRUE(cJSON_GetNumberValue(capJ) >= 0.0 || cJSON_GetStringValue(capJ));
652    }
653}
654
655static void CheckServices(const cJSON *fileRoot)
656{
657    int servArrSize = 0;
658    cJSON *serviceArr = GetArrItem(fileRoot, servArrSize, SERVICE_ARR_NAME_IN_JSON);
659    EXPECT_TRUE(serviceArr != nullptr);
660    EXPECT_TRUE(servArrSize <= MAX_SERVICES_COUNT_IN_FILE);
661
662    for (int i = 0; i < servArrSize; ++i) {
663        cJSON *curItem = cJSON_GetArrayItem(serviceArr, i);
664        EXPECT_TRUE(curItem != nullptr);
665        CheckService(curItem);
666    }
667}
668
669static void CheckCmd(const TestCmdLine *resCmd)
670{
671    EXPECT_TRUE(strlen(resCmd->name) > 0);
672    EXPECT_TRUE(strlen(resCmd->cmdContent) > 0);
673
674    if (strcmp("start ", resCmd->name) == 0) {
675        for (size_t i = 0; i < strlen(resCmd->cmdContent); ++i) {
676            EXPECT_NE(' ', resCmd->cmdContent[i]);    // no spaces in service name
677        }
678    } else if (strcmp("mkdir ", resCmd->name) == 0) {
679        for (size_t i = 0; i < strlen(resCmd->cmdContent); ++i) {
680            EXPECT_NE('.', resCmd->cmdContent[i]);    // no dots in path string
681        }
682    } else if (strcmp("chmod ", resCmd->name) == 0) {
683        EXPECT_TRUE(strlen(resCmd->cmdContent) >= 6);    // 0xxx x    at least 6 characters
684        EXPECT_EQ('0', resCmd->cmdContent[0]);
685        EXPECT_EQ(' ', resCmd->cmdContent[4]);    // 4 bytes, after 0xxx must be space
686        for (int i = 1; i < 4; ++i) {    // 4 bytes, 0xxx, xxx must be digits
687            EXPECT_TRUE(resCmd->cmdContent[i] >= '0' && resCmd->cmdContent[i] <= '7');
688        }
689        for (size_t i = 5; i < strlen(resCmd->cmdContent); ++i) {    // target starts from index 5
690            EXPECT_NE(' ', resCmd->cmdContent[i]);    // no spaces allowed
691        }
692    } else if (strcmp("chown ", resCmd->name) == 0) {
693        EXPECT_TRUE(strlen(resCmd->cmdContent) >= 5);    // x y z   at least 5 characters
694        EXPECT_NE(' ', resCmd->cmdContent[0]);           // should not start with space
695        EXPECT_NE(' ', resCmd->cmdContent[strlen(resCmd->cmdContent) - 1]);  // should not end with space
696        size_t spacePos = 0;
697        size_t spaceCnt = 0;
698        for (size_t i = 1; i < strlen(resCmd->cmdContent); ++i) {
699            if (resCmd->cmdContent[i] != ' ') {
700                continue;
701            }
702            ++spaceCnt;
703            if (spacePos != 0) {
704                EXPECT_NE(spacePos + 1, i);    // consecutive spaces should not appear
705            }
706            spacePos = i;
707        }
708        EXPECT_EQ(spaceCnt, 2);    // 2 spaces allowed in cmd content
709    } else if (strcmp("mount ", resCmd->name) == 0) {
710        EXPECT_NE(' ', resCmd->cmdContent[0]);    // should not start with space
711    } else if (strcmp("loadcfg ", resCmd->name) == 0) {
712        EXPECT_NE(' ', resCmd->cmdContent[0]);   // should not start with space
713    } else if (strcmp("export ", resCmd->name) == 0) {
714        EXPECT_NE(' ', resCmd->cmdContent[0]);   // should not start with space
715    }  else if (strcmp("exec ", resCmd->name) == 0) {
716        EXPECT_NE(' ', resCmd->cmdContent[0]);   // should not start with space
717    } else {    // unknown cmd
718        EXPECT_TRUE(false);
719    }
720}
721
722static void CheckJob(const cJSON *jobItem)
723{
724    if (jobItem == nullptr) {
725        return;
726    }
727
728    cJSON *cmdsItem = cJSON_GetObjectItem(jobItem, CMDS_ARR_NAME_IN_JSON.c_str());
729    EXPECT_TRUE(cmdsItem != nullptr);
730    EXPECT_TRUE(cJSON_IsArray(cmdsItem));
731
732    int cmdLinesCnt = cJSON_GetArraySize(cmdsItem);
733    EXPECT_TRUE(cmdLinesCnt <= MAX_CMD_CNT_IN_ONE_JOB);
734
735    for (int i = 0; i < cmdLinesCnt; ++i) {
736        char *cmdLineStr = cJSON_GetStringValue(cJSON_GetArrayItem(cmdsItem, i));
737        EXPECT_TRUE(cmdLineStr != nullptr);
738        EXPECT_TRUE(strlen(cmdLineStr) > 0);
739
740        TestCmdLine resCmd;
741        (void)memset_s(&resCmd, sizeof(resCmd), 0, sizeof(resCmd));
742        ParseCmdLine(cmdLineStr, &resCmd);
743        CheckCmd(&resCmd);
744    }
745}
746
747static void CheckJobs(const cJSON* fileRoot)
748{
749    int jobArrSize = 0;
750    cJSON *jobArr = GetArrItem(fileRoot, jobArrSize, JOBS_ARR_NAME_IN_JSON);
751    EXPECT_TRUE(jobArr != nullptr);
752    EXPECT_TRUE(jobArrSize == JOBS_IN_FILE_COUNT);
753
754    bool findPreInit = false;
755    bool findInit = false;
756    bool findPostInit = false;
757    for (int i = 0; i < jobArrSize; ++i) {
758        cJSON *jobItem = cJSON_GetArrayItem(jobArr, i);
759        EXPECT_TRUE(jobItem != nullptr);
760        char *jobNameStr = cJSON_GetStringValue(cJSON_GetObjectItem(jobItem, "name"));
761        EXPECT_TRUE(jobNameStr != nullptr);
762        if (strcmp(jobNameStr, "pre-init") == 0) {
763            findPreInit = true;
764        } else if (strcmp(jobNameStr, "init") == 0) {
765            findInit = true;
766        }  else if (strcmp(jobNameStr, "post-init") == 0) {
767            findPostInit = true;
768        } else {
769            EXPECT_TRUE(false);    // unknown job name
770            continue;
771        }
772
773        CheckJob(jobItem);
774    }
775
776    EXPECT_TRUE(findPreInit && findInit && findPostInit);
777}
778
779/*
780 * @tc.name: cfgCheckContent_001
781 * @tc.desc: init.cfg file content check
782 * @tc.type: FUNC
783 */
784HWTEST_F(StartupInitUTest, cfgCheckContent_001, TestSize.Level0)
785{
786    char *fileBuf = ReadFileToBuf();
787    if (fileBuf == nullptr) {
788        EXPECT_TRUE(fileBuf != nullptr);
789        return;
790    }
791
792    cJSON *fileRoot = cJSON_Parse(fileBuf);
793    free(fileBuf);
794    fileBuf = nullptr;
795
796    EXPECT_TRUE(fileRoot != nullptr);
797
798    CheckServices(fileRoot);
799    CheckJobs(fileRoot);
800    cJSON_Delete(fileRoot);
801    fileRoot = nullptr;
802}
803
804/*
805 * @tc.name: CreateIllegalCfg
806 * @tc.desc: Create illegal Config file for testing
807 * @tc.type: FUNC
808 */
809static void CreateIllegalCfg()
810{
811    FILE *testCfgFile = fopen(TEST_CFG_ILLEGAL.c_str(), "w+");
812    if (testCfgFile == nullptr) {
813        return;
814    }
815
816    std::string writeContent = "mount zpfs /patch/etc:/etc /etc";
817    if (fwrite(writeContent.c_str(), writeContent.length(), 1, testCfgFile) != 1) {
818        (void)fclose(testCfgFile);
819        return;
820    }
821
822    (void)fclose(testCfgFile);
823}
824
825/*
826 * @tc.name: cmdFuncDoLoadCfgTest_001
827 * @tc.desc: parse function, parse success test
828 * @tc.type: FUNC
829 */
830HWTEST_F(StartupInitUTest, cmdFuncDoLoadCfgTest_001, TestSize.Level0)
831{
832    TestCmdLine curCmdLine;
833    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
834
835    ParseCmdLine("loadcfg /patch/fstab.cfg", &curCmdLine);
836    EXPECT_EQ(0, strcmp("loadcfg ", curCmdLine.name));
837    EXPECT_EQ(0, strcmp("/patch/fstab.cfg", curCmdLine.cmdContent));
838};
839
840/*
841 * @tc.name: cmdFuncDoLoadCfgTest_002
842 * @tc.desc: fstab.cfg file fail test
843 * @tc.type: FUNC
844 */
845HWTEST_F(StartupInitUTest, cmdFuncDoLoadCfgTest_002, TestSize.Level0)
846{
847    TestCmdLine curCmdLine;
848    std::string cmdStr = "loadcfg ";
849    std::string cmdContentStr = "/patch/file_not_exist.cfg";
850    struct stat testCfgStat = {0};
851
852    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
853    std::string command = cmdStr + cmdContentStr;
854    ParseCmdLine(command.c_str(), &curCmdLine);
855    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
856    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
857    stat(cmdContentStr.c_str(), &testCfgStat);
858    EXPECT_TRUE(testCfgStat.st_size == 0);
859    DoCmd(&curCmdLine);
860
861    cmdContentStr = TEST_CFG_ILLEGAL;
862    CreateIllegalCfg();
863    (void)memset_s(&curCmdLine, sizeof(curCmdLine), 0, sizeof(curCmdLine));
864    command = cmdStr + cmdContentStr;
865    ParseCmdLine(command.c_str(), &curCmdLine);
866    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
867    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
868    EXPECT_EQ(0, stat(cmdContentStr.c_str(), &testCfgStat));
869    EXPECT_TRUE(testCfgStat.st_size > 0);
870    DoCmd(&curCmdLine);
871
872    remove(TEST_CFG_ILLEGAL.c_str());
873}
874
875/*
876 * @tc.name: cmdFuncDoLoadCfgTest_003
877 * @tc.desc: fstab.cfg file success test
878 * @tc.type: FUNC
879 */
880HWTEST_F(StartupInitUTest, cmdFuncDoLoadCfgTest_003, TestSize.Level0)
881{
882    TestCmdLine curCmdLine;
883    std::string cmdStr = "loadcfg ";
884    std::string cmdContentStr = "/patch/fstab.cfg";
885    char buf[CAT_BUF_SIZE] = {0};
886    struct stat testCfgStat = {0};
887    FILE *fd = nullptr;
888    size_t size;
889    bool hasZpfs = false;
890    std::string command = cmdStr + cmdContentStr;
891    ParseCmdLine(command.c_str(), &curCmdLine);
892    EXPECT_EQ(0, strcmp(cmdStr.c_str(), curCmdLine.name));
893    EXPECT_EQ(0, strcmp(cmdContentStr.c_str(), curCmdLine.cmdContent));
894
895    DoCmd(&curCmdLine);
896
897    stat(cmdContentStr.c_str(), &testCfgStat);
898    if (testCfgStat.st_size > 0) {
899        fd = fopen(TEST_PROC_MOUNTS.c_str(), "r");
900
901        if (fd == nullptr) {
902            EXPECT_TRUE(fd != nullptr);
903            return;
904        }
905
906        do {
907            size = fread(buf, 1, CAT_BUF_SIZE - 1, fd);
908            if (size < 0) {
909                EXPECT_TRUE(size >= 0);
910                break;
911            }
912            buf[CAT_BUF_SIZE - 1] = 0;
913            if (strstr(buf, "zpfs") != nullptr) {
914                hasZpfs = true;
915                break;
916            }
917        } while (size > 0);
918        EXPECT_TRUE(hasZpfs);
919        fclose(fd);
920    }
921}
922
923/*
924 * @tc.name: cmdJobTest_001
925 * @tc.desc: job functions test
926 * @tc.type: FUNC
927 */
928HWTEST_F(StartupInitUTest, cmdJobTest_001, TestSize.Level0)
929{
930    // functions do not crash
931    ParseAllJobs(nullptr, nullptr);
932    DoJob(nullptr);
933    DoJob("job name does not exist");
934    ReleaseAllJobs();
935    StartServiceByName("service name does not exist");
936    StopAllServices(0, nullptr, 0, nullptr);
937    ServiceReap(nullptr);
938    EXPECT_NE(0, ServiceStart(nullptr, nullptr));
939    EXPECT_NE(0, ServiceStop(nullptr));
940}
941
942/*
943 * @tc.name: cmdJobTest_002
944 * @tc.desc: job functions test
945 * @tc.type: FUNC
946 */
947HWTEST_F(StartupInitUTest, cmdJobTest_002, TestSize.Level0)
948{
949    std::string cfgJson = "{\"jobs\":[{\"name\":\"pre-init\",\"cmds\":[\"mkdir " +
950        PRE_INIT_DIR + "\"]},{\"name\":\"init\",\"cmds\":[\"mkdir " + INIT_DIR +
951        "\"]},{\"name\":\"post-init\",\"cmds\":[\"mkdir " + POST_INIT_DIR + "\"]}]}";
952    cJSON* jobItem = cJSON_Parse(cfgJson.c_str());
953    EXPECT_NE(nullptr, jobItem);
954    if (jobItem == nullptr) {
955        return;
956    }
957    ConfigContext context = { INIT_CONTEXT_MAIN };
958    ParseAllJobs(jobItem, &context);
959    DoJob("pre-init");
960    DoJob("init");
961    DoJob("post-init");
962
963    // check if dir exists
964    struct stat postDirStat = {0};
965    EXPECT_EQ(0, stat(POST_INIT_DIR.c_str(), &postDirStat));
966
967    // release resource
968    cJSON_Delete(jobItem);
969    if (remove(POST_INIT_DIR.c_str()) != 0 ||
970        remove(INIT_DIR.c_str()) != 0 ||
971        remove(PRE_INIT_DIR.c_str()) != 0) {
972    }
973    ReleaseAllJobs();
974}
975}  // namespace OHOS
976