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 
32 using namespace std;
33 using namespace testing::ext;
34 
35 namespace OHOS {
36 std::vector<std::string> g_supportedCmds;
37 const std::string ROOT_DIR = "/storage/data/";
38 const std::string TEST_DRI = ROOT_DIR + "StartInitTestDir";
39 const std::string TEST_FILE = TEST_DRI + "/test.txt";
40 const std::string TEST_CFG_ILLEGAL = TEST_DRI + "/illegal.cfg";
41 const std::string TEST_PROC_MOUNTS = "/proc/mounts";
42 #ifndef USE_EMMC_STORAGE
43 const uid_t TEST_FILE_UID = 999;
44 const gid_t TEST_FILE_GID = 999;
45 const mode_t TEST_FILE_MODE = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
46 #endif
47 
48 // init.cfg related
49 const std::string CFG_FILE = "/etc/init.cfg";
50 const std::string SERVICE_ARR_NAME_IN_JSON = "services";
51 const std::string JOBS_ARR_NAME_IN_JSON = "jobs";
52 const std::string CMDS_ARR_NAME_IN_JSON = "cmds";
53 const uid_t CFG_FILE_UID = 0;
54 const gid_t CFG_FILE_GID = 0;
55 const mode_t CFG_FILE_MODE = S_IRUSR;
56 const int JOBS_IN_FILE_COUNT = 3; // pre-init, init, post-init
57 const int MAX_SERVICES_COUNT_IN_FILE = 100;
58 const int MAX_CAPS_CNT_FOR_ONE_SERVICE = 100;
59 const unsigned int MAX_JSON_FILE_LEN = 102400;        // max init.cfg size 100KB
60 const int TEST_MAX_PATH_ARGS_CNT = 20;                // max path and args count
61 const int TEST_MAX_ONE_ARG_LEN = 64;                  // max length of one param/path
62 const int CAT_BUF_SIZE = 512;                         // standard Cat buffer size from vfs_shell_cmd
63 
64 // job test related
65 const std::string PRE_INIT_DIR = ROOT_DIR + "preInitDir/";
66 const std::string INIT_DIR = PRE_INIT_DIR + "initDir";
67 const std::string POST_INIT_DIR = INIT_DIR + "postInitDir";
68 
69 using  TestCmdLine = struct {
70     char name[MAX_CMD_NAME_LEN + 1];
71     char cmdContent[MAX_CMD_CONTENT_LEN + 1];
72 };
73 
74 class StartupInitUTest : public testing::Test {
75 public:
SetUpTestCase()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 
TearDownTestCase()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     }
SetUp()125     void SetUp()
126     {
127         EnableInitLog(INIT_FATAL);
128     }
TearDown()129     void TearDown() {}
130 };
131 
ParseCmdLine(const char * content,TestCmdLine * resCmd)132 void 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 
DoCmd(const TestCmdLine * resCmd)155 void 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  */
170 HWTEST_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  */
181 HWTEST_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  */
208 HWTEST_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  */
225 HWTEST_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  */
265 HWTEST_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  */
296 HWTEST_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  */
307 HWTEST_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  */
326 HWTEST_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  */
371 HWTEST_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  */
427 HWTEST_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  */
463 HWTEST_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  */
525 HWTEST_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 
ReadFileToBuf()536 static 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 
GetArrItem(const cJSON * fileRoot,int & arrSize,const std::string & arrName)568 static 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 
IsForbidden(const char * fieldStr)578 static 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 
CheckService(const cJSON * curItem)600 static 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 
CheckServices(const cJSON * fileRoot)655 static 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 
CheckCmd(const TestCmdLine * resCmd)669 static 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 
CheckJob(const cJSON * jobItem)722 static 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 
CheckJobs(const cJSON * fileRoot)747 static 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  */
784 HWTEST_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  */
CreateIllegalCfg()809 static 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  */
830 HWTEST_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  */
845 HWTEST_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  */
880 HWTEST_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  */
928 HWTEST_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  */
947 HWTEST_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