1 /*
2 * Copyright (c) 2021 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
16 #include <sys/socket.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include <cstdlib>
20 #include "init_cmds.h"
21 #include "init_service.h"
22 #include "init_service_manager.h"
23 #include "init_service_socket.h"
24 #include "param_stub.h"
25 #include "init_utils.h"
26 #include "securec.h"
27 #include "init_group_manager.h"
28 #include "trigger_manager.h"
29 #include "bootstage.h"
30 #include "init_hook.h"
31 #include "plugin_adapter.h"
32
33 const char *SERVICE_INFO_JSONSTR = "{"
34 "\"services\":{"
35 " \"name\":\"test_service\","
36 " \"path\":[\"/data/init_ut/test_service\"],"
37 " \"importance\":-20,"
38 " \"uid\":\"system\","
39 " \"writepid\":[\"/dev/test_service\"],"
40 " \"console\":1,"
41 " \"caps\":[\"TEST_ERR\"],"
42 " \"gid\":[\"system\"],"
43 " \"period\":2,"
44 " \"env\":[{ "
45 " \"name\":\"test\","
46 " \"value\":\"xxxx\""
47 " }],"
48 " \"critical\":1,"
49 " \"jobs\" : {"
50 " \"pre-start\":\"test_service:pre-start\""
51 " }"
52 "}"
53 "}";
54 const char *JOB_INFO_JSONSTR = "{"
55 "\"jobs\": ["
56 " {"
57 " \"name\" : \"test_service:pre-start\","
58 " \"cmds\" : ["
59 " \"setparam test.test.prestartKey 1\""
60 " ]"
61 " }"
62 "]"
63 "}";
64 const char *PRE_START_KEY = "test.test.prestartKey";
DoCmdByName(const char * name,const char * cmdContent)65 static void DoCmdByName(const char *name, const char *cmdContent)
66 {
67 int cmdIndex = 0;
68 (void)GetMatchCmd(name, &cmdIndex);
69 DoCmdByIndex(cmdIndex, cmdContent, nullptr);
70 }
71
LoadJobs()72 static void LoadJobs()
73 {
74 cJSON *jobJson = cJSON_Parse(JOB_INFO_JSONSTR);
75 INIT_ERROR_CHECK(jobJson != NULL, return, "Failed to loadJobs");
76 ParseTriggerConfig(jobJson, nullptr, nullptr);
77 cJSON_Delete(jobJson);
78 }
79
ServiceTestTriggerExe(const TriggerNode * trigger,const char * content,uint32_t size)80 static int ServiceTestTriggerExe(const TriggerNode *trigger, const char *content, uint32_t size)
81 {
82 INIT_ERROR_CHECK(trigger != NULL, return -1, "ParamWriteTriggerExe trigger is NULL");
83 CommandNode *cmd = GetNextCmdNode((JobNode *)trigger, NULL);
84 while (cmd != NULL) {
85 DoCmdByIndex(cmd->cmdKeyIndex, cmd->content, &cmd->cfgContext);
86 cmd = GetNextCmdNode((JobNode *)trigger, cmd);
87 }
88 return 0;
89 }
90
91 using namespace testing::ext;
92 using namespace std;
93 namespace init_ut {
94 class ServiceUnitTest : public testing::Test {
95 public:
SetUpTestCase(void)96 static void SetUpTestCase(void)
97 {
98 string svcPath = "/data/init_ut/test_service";
99 auto fp = std::unique_ptr<FILE, decltype(&fclose)>(fopen(svcPath.c_str(), "wb"), fclose);
100 if (fp == nullptr) {
101 cout << "ServiceUnitTest open : " << svcPath << " failed." << errno << endl;
102 }
103 sync();
104 }
105
TearDownTestCase(void)106 static void TearDownTestCase(void) {};
SetUp()107 void SetUp() {};
TearDown()108 void TearDown() {};
109 };
110
111 HWTEST_F(ServiceUnitTest, case01, TestSize.Level1)
112 {
113 cJSON* jobItem = cJSON_Parse(SERVICE_INFO_JSONSTR);
114 ASSERT_NE(nullptr, jobItem);
115 cJSON *serviceItem = cJSON_GetObjectItem(jobItem, "services");
116 ASSERT_NE(nullptr, serviceItem);
117 Service *service = AddService("test_service");
118 int ret = ParseOneService(serviceItem, service);
119 EXPECT_EQ(ret, 0);
120
121 LoadJobs();
122 RegisterTriggerExec(TRIGGER_UNKNOW, ServiceTestTriggerExe);
123
124 ret = ServiceStart(service, &service->pathArgs);
125 EXPECT_EQ(ret, 0);
126
127 char value[PARAM_VALUE_LEN_MAX] = {0};
128 u_int32_t len = sizeof(value);
129 ret = SystemGetParameter(PRE_START_KEY, value, &len);
130 EXPECT_EQ(ret, 0);
131 EXPECT_EQ(0, strcmp("1", value));
132
133 ret = ServiceStop(service);
134 EXPECT_EQ(ret, 0);
135
136 ReleaseService(service);
137 cJSON_Delete(jobItem);
138 }
139
140 HWTEST_F(ServiceUnitTest, case02, TestSize.Level1)
141 {
142 const char *jsonStr = "{\"services\":{\"name\":\"test_service8\",\"path\":[\"/data/init_ut/test_service\"],"
143 "\"importance\":-20,\"uid\":\"system\",\"writepid\":[\"/dev/test_service\"],\"console\":1,"
144 "\"gid\":[\"system\", \"shell\", \"root\"],\"caps\":[10, 4294967295, 10000],\"cpucore\":[1]}}";
145 cJSON* jobItem = cJSON_Parse(jsonStr);
146 ASSERT_NE(nullptr, jobItem);
147 cJSON *serviceItem = cJSON_GetObjectItem(jobItem, "services");
148 ASSERT_NE(nullptr, serviceItem);
149 Service *service = AddService("test_service8");
150 ASSERT_NE(nullptr, service);
151 int ret = ParseOneService(serviceItem, service);
152 EXPECT_EQ(ret, 0);
153
154 int fds[1] = {-1}; // ServiceStop will release fds
155 UpdaterServiceFds(service, fds, 1);
156 service->attribute = SERVICE_ATTR_ONDEMAND;
157 ret = ServiceStart(service, &service->pathArgs);
158 EXPECT_EQ(ret, 0);
159 CmdLines *cmdline = (CmdLines *)malloc(sizeof(CmdLines) + sizeof(CmdLine));
160 ASSERT_NE(nullptr, cmdline);
161 cmdline->cmdNum = 1;
162 cmdline->cmds[0].cmdIndex = 0;
163 service->restartArg = cmdline;
164 ServiceSocket tmpSock = { .next = nullptr, .sockFd = 0 };
165 ServiceSocket tmpSock1 = { .next = &tmpSock, .sockFd = 0 };
166 service->socketCfg = &tmpSock1;
167 ServiceReap(service);
168 service->socketCfg = nullptr;
169 service->attribute &= SERVICE_ATTR_NEED_RESTART;
170 service->firstCrashTime = 0;
171 ServiceReap(service);
172 DoCmdByName("reset ", "test_service8");
173 // reset again
174 DoCmdByName("reset ", "test_service8");
175 service->pid = 0xfffffff; // 0xfffffff is not exist pid
176 service->attribute = SERVICE_ATTR_TIMERSTART;
177 ret = ServiceStop(service);
178 EXPECT_EQ(ret, 0);
179 ReleaseService(service);
180 cJSON_Delete(jobItem);
181 }
182
183 HWTEST_F(ServiceUnitTest, TestServiceStartAbnormal, TestSize.Level1)
184 {
185 const char *jsonStr = "{\"services\":{\"name\":\"test_service1\",\"path\":[\"/data/init_ut/test_service\"],"
186 "\"importance\":-20,\"uid\":\"system\",\"writepid\":[\"/dev/test_service\"],\"console\":1,"
187 "\"gid\":[\"system\"],\"caps\":[\"\"]}}";
188 cJSON* jobItem = cJSON_Parse(jsonStr);
189 ASSERT_NE(nullptr, jobItem);
190 cJSON *serviceItem = cJSON_GetObjectItem(jobItem, "services");
191 ASSERT_NE(nullptr, serviceItem);
192 Service *service = AddService("test_service1");
193 ASSERT_NE(nullptr, service);
194 int ret = ParseOneService(serviceItem, service);
195 EXPECT_EQ(ret, 0);
196
197 ret = ServiceStart(service, &service->pathArgs);
198 EXPECT_EQ(ret, 0);
199
200 service->attribute &= SERVICE_ATTR_INVALID;
201 ret = ServiceStart(service, &service->pathArgs);
202 EXPECT_EQ(ret, 0);
203
204 service->pid = -1;
205 ret = ServiceStop(service);
206 EXPECT_EQ(ret, 0);
207 ReleaseService(service);
208 cJSON_Delete(jobItem);
209 }
210
211 HWTEST_F(ServiceUnitTest, TestServiceReap, TestSize.Level1)
212 {
213 Service *service = AddService("test_service2");
214 ASSERT_NE(nullptr, service);
215 EXPECT_EQ(service->attribute, 0);
216 service->attribute = SERVICE_ATTR_ONDEMAND;
217 ServiceReap(service);
218 service->attribute = 0;
219
220 service->restartArg = (CmdLines *)calloc(1, sizeof(CmdLines));
221 ASSERT_NE(nullptr, service->restartArg);
222 ServiceReap(service);
223 EXPECT_EQ(service->attribute, SERVICE_ATTR_TIMERSTART);
224
225 const int crashCount = 241;
226 service->crashCnt = crashCount;
227 ServiceReap(service);
228 EXPECT_EQ(service->attribute, SERVICE_ATTR_TIMERSTART);
229
230 service->attribute |= SERVICE_ATTR_ONCE;
231 ServiceReap(service);
232 EXPECT_EQ(service->attribute, (SERVICE_ATTR_ONCE | SERVICE_ATTR_TIMERSTART));
233 service->attribute = SERVICE_ATTR_CRITICAL;
234 service->crashCount = 1;
235 ServiceReap(service);
236 ReleaseService(service);
237 }
238
239 HWTEST_F(ServiceUnitTest, TestServiceReapOther, TestSize.Level1)
240 {
241 const char *serviceStr = "{\"services\":{\"name\":\"test_service4\",\"path\":[\"/data/init_ut/test_service\"],"
242 "\"onrestart\":[\"sleep 1\"],\"console\":1,\"writepid\":[\"/dev/test_service\"]}}";
243
244 cJSON* jobItem = cJSON_Parse(serviceStr);
245 ASSERT_NE(nullptr, jobItem);
246 cJSON *serviceItem = cJSON_GetObjectItem(jobItem, "services");
247 ASSERT_NE(nullptr, serviceItem);
248 Service *service = AddService("test_service4");
249 ASSERT_NE(nullptr, service);
250 int ret = ParseOneService(serviceItem, service);
251 EXPECT_EQ(ret, 0);
252
253 ServiceReap(service);
254 EXPECT_NE(service->attribute, 0);
255
256 service->attribute |= SERVICE_ATTR_CRITICAL;
257 ServiceReap(service);
258 EXPECT_NE(service->attribute, 0);
259
260 service->attribute |= SERVICE_ATTR_NEED_STOP;
261 ServiceReap(service);
262 EXPECT_NE(service->attribute, 0);
263
264 service->attribute |= SERVICE_ATTR_INVALID;
265 ServiceReap(service);
266 EXPECT_NE(service->attribute, 0);
267
268 ret = ServiceStop(service);
269 EXPECT_EQ(ret, 0);
270 ReleaseService(service);
271 cJSON_Delete(jobItem);
272 }
273
274 HWTEST_F(ServiceUnitTest, TestServiceManagerRelease, TestSize.Level1)
275 {
276 Service *service = nullptr;
277 ReleaseService(service);
278 EXPECT_TRUE(service == nullptr);
279 service = AddService("test_service5");
280 ASSERT_NE(nullptr, service);
281 service->pathArgs.argv = (char **)malloc(sizeof(char *));
282 ASSERT_NE(nullptr, service->pathArgs.argv);
283 service->pathArgs.count = 1;
284 const char *path = "/data/init_ut/test_service_release";
285 service->pathArgs.argv[0] = strdup(path);
286
287 service->writePidArgs.argv = (char **)malloc(sizeof(char *));
288 ASSERT_NE(nullptr, service->writePidArgs.argv);
289 service->writePidArgs.count = 1;
290 service->writePidArgs.argv[0] = strdup(path);
291
292 service->servPerm.caps = (unsigned int *)malloc(sizeof(unsigned int));
293 ASSERT_NE(nullptr, service->servPerm.caps);
294 service->servPerm.gIDArray = (gid_t *)malloc(sizeof(gid_t));
295 ASSERT_NE(nullptr, service->servPerm.gIDArray);
296 service->socketCfg = (ServiceSocket *)malloc(sizeof(ServiceSocket));
297 ASSERT_NE(nullptr, service->socketCfg);
298 service->socketCfg->sockFd = 0;
299 service->socketCfg->next = nullptr;
300 service->fileCfg = (ServiceFile *)malloc(sizeof(ServiceFile));
301 ASSERT_NE(nullptr, service->fileCfg);
302 service->fileCfg->fd = 0;
303 service->fileCfg->next = nullptr;
304 service->env = (ServiceEnv*)malloc(sizeof(ServiceEnv));
305 ASSERT_NE(nullptr, service->env);
306 int ret = strcpy_s(service->env->name, MAX_ENV_NAME, "test5");
307 EXPECT_EQ(ret, EOK);
308 ret = strcpy_s(service->env->value, MAX_ENV_VALUE, "xxxxx");
309 EXPECT_EQ(ret, EOK);
310 ReleaseService(service);
311 service = nullptr;
312 }
313
314 HWTEST_F(ServiceUnitTest, TestServiceManagerGetService, TestSize.Level1)
315 {
316 Service *service = GetServiceByPid(1);
317 StopAllServices(1, nullptr, 0, nullptr);
318 EXPECT_TRUE(service == nullptr);
319 const char *jsonStr = "{\"services\":{\"name\":\"test_service2\",\"path\":[\"/data/init_ut/test_service\"],"
320 "\"importance\":-20,\"uid\":\"system\",\"writepid\":[\"/dev/test_service\"],\"console\":1,"
321 "\"gid\":[\"system\"], \"critical\":[1,2]}}";
322 cJSON* jobItem = cJSON_Parse(jsonStr);
323 ASSERT_NE(nullptr, jobItem);
324 cJSON *serviceItem = cJSON_GetObjectItem(jobItem, "services");
325 ASSERT_NE(nullptr, serviceItem);
326 service = AddService("test_service2");
327 ASSERT_NE(nullptr, service);
328 int ret = ParseOneService(serviceItem, service);
329 EXPECT_NE(ret, 0);
330 cJSON_Delete(jobItem);
331 const char *jsonStr1 = "{\"services\":{\"name\":\"test_service3\",\"path\":[\"/data/init_ut/test_service\"],"
332 "\"importance\":-20,\"uid\":\"system\",\"writepid\":[\"/dev/test_service\"],\"console\":1,"
333 "\"gid\":[\"system\"], \"critical\":[\"on\"]}}";
334 jobItem = cJSON_Parse(jsonStr1);
335 ASSERT_NE(nullptr, jobItem);
336 serviceItem = cJSON_GetObjectItem(jobItem, "services");
337 ASSERT_NE(nullptr, serviceItem);
338 service = AddService("test_service3");
339 ASSERT_NE(nullptr, service);
340 ret = ParseOneService(serviceItem, service);
341 EXPECT_NE(ret, 0);
342 cJSON_Delete(jobItem);
343 }
344
345 /**
346 * @tc.name: TestServiceBootEventHook
347 * @tc.desc: test bootevent module exec correct
348 * @tc.type: FUNC
349 * @tc.require: issueI5NTX4
350 * @tc.author:
351 */
352 HWTEST_F(ServiceUnitTest, TestServiceBootEventHook, TestSize.Level1)
353 {
354 const char *serviceStr = "{"
355 "\"services\": [{"
356 "\"name\" : \"test-service\","
357 "\"path\" : [\"/dev/test_service\"],"
358 "\"start-mode\" : \"condition\","
359 "\"writepid\":[\"/dev/test_service\"],"
360 "\"bootevents\" : \"bootevent2\""
361 "},{"
362 "\"name\" : \"test-service\","
363 "\"path\" : [\"/dev/test_service\"],"
364 "\"start-mode\" : \"condition\","
365 "\"writepid\":[\"/dev/test_service\"],"
366 "\"bootevents\" : \"bootevent.bootevent2\""
367 "},{"
368 "\"name\" : \"test-service2\","
369 "\"path\" : [\"/dev/test_service\"],"
370 "\"console\":1,"
371 "\"start-mode\" : \"boot\","
372 "\"writepid\":[\"/dev/test_service\"],"
373 "\"bootevents\" : [\"bootevent.bootevent1\", \"bootevent.bootevent2\"]"
374 "}]"
375 "}";
376
377 SERVICE_INFO_CTX serviceInfoContext;
378 serviceInfoContext.serviceName = "test-service2";
379 serviceInfoContext.reserved = "bootevent";
380 HookMgrExecute(GetBootStageHookMgr(), INIT_GLOBAL_INIT, nullptr, nullptr);
381 (void)HookMgrExecute(GetBootStageHookMgr(), INIT_SERVICE_DUMP, (void *)(&serviceInfoContext), nullptr);
382 cJSON *fileRoot = cJSON_Parse(serviceStr);
383 ASSERT_NE(nullptr, fileRoot);
384 ConfigContext context = { INIT_CONTEXT_MAIN };
385 ParseAllServices(fileRoot, &context);
386 (void)HookMgrExecute(GetBootStageHookMgr(), INIT_SERVICE_FORK_BEFORE, (void *)(&serviceInfoContext), nullptr);
387 serviceInfoContext.reserved = nullptr;
388 (void)HookMgrExecute(GetBootStageHookMgr(), INIT_SERVICE_FORK_BEFORE, (void *)(&serviceInfoContext), nullptr);
389 const char *initBootevent[] = {"init", "test"};
390 PluginExecCmd("bootevent", ARRAY_LENGTH(initBootevent), initBootevent);
391 const char *initBooteventDup[] = {"init", "test"};
392 PluginExecCmd("bootevent", ARRAY_LENGTH(initBooteventDup), initBooteventDup);
393 const char *initBooteventErr[] = {"init"};
394 PluginExecCmd("bootevent", ARRAY_LENGTH(initBooteventErr), initBooteventErr);
395 SystemWriteParam("bootevent.bootevent1", "true");
396 SystemWriteParam("bootevent.bootevent2", "true");
397 SystemWriteParam("bootevent.bootevent2", "true");
398 SystemWriteParam("persist.init.bootevent.enable", "false");
399 HookMgrExecute(GetBootStageHookMgr(), INIT_POST_PERSIST_PARAM_LOAD, nullptr, nullptr);
400 cJSON_Delete(fileRoot);
401 }
402
403 HWTEST_F(ServiceUnitTest, TestSetServiceContent, TestSize.Level1)
404 {
405 (void)WatchConsoleDevice(nullptr);
406 Service service;
407 (void)WatchConsoleDevice(&service);
408 int fd = open("/dev/console", O_RDWR);
409 if (fd >= 0) {
410 (void)write(fd, "0", 1);
411 (void)close(fd);
412 }
413 PluginExecCmdByName("setServiceContent", "netmanager");
414 StopServiceByName("netmanager");
415 }
416
417 HWTEST_F(ServiceUnitTest, TestServiceExec, TestSize.Level1)
418 {
419 Service *service = AddService("test_service7");
420 ASSERT_NE(nullptr, service);
421
422 service->pathArgs.argv = (char **)malloc(sizeof(char *));
423 ASSERT_NE(service->pathArgs.argv, nullptr);
424 service->pathArgs.count = 1;
425 const char *path = "/data/init_ut/test_service_release";
426 service->pathArgs.argv[0] = strdup(path);
427 service->importance = 20;
428 service->servPerm.gIDCnt = -1;
429 service->servPerm.uID = 0;
430 unsigned int *caps = (unsigned int *)calloc(1, sizeof(unsigned int) * 1);
431 ASSERT_NE(nullptr, caps);
432 caps[0] = FULL_CAP;
433 service->servPerm.caps = caps;
434 service->servPerm.capsCnt = 1;
435 IsEnableSandbox();
436 EnterServiceSandbox(service);
437 int ret = ServiceExec(service, &service->pathArgs);
438 EXPECT_EQ(ret, 0);
439
440 const int invalidImportantValue = 20;
441 ret = SetImportantValue(service, "", invalidImportantValue, 1);
442 EXPECT_EQ(ret, -1);
443 ReleaseService(service);
444 }
445 } // namespace init_ut
446