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 #include "bootevent.h"
16
17 #include <stdbool.h>
18 #include "init_module_engine.h"
19 #include "init_cmdexecutor.h"
20 #include "trigger_manager.h"
21 #include "init_log.h"
22 #include "plugin_adapter.h"
23 #include "init_hook.h"
24 #include "init_service.h"
25 #include "bootstage.h"
26 #include "securec.h"
27 #include "init_utils.h"
28 #include "init_cmds.h"
29
30 static int g_bootEventNum = 0;
31 // check bootevent enable
32 static int g_bootEventEnable = 1;
33 static ListNode bootEventList = {&bootEventList, &bootEventList};
34
BootEventParaListCompareProc(ListNode * node,void * data)35 static int BootEventParaListCompareProc(ListNode *node, void *data)
36 {
37 BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)node;
38 if (strncmp(item->paramName, BOOT_EVENT_PARA_PREFIX, BOOT_EVENT_PARA_PREFIX_LEN) != 0) {
39 return -1;
40 }
41 if (strcmp(item->paramName + BOOT_EVENT_PARA_PREFIX_LEN, (const char *)data) == 0) {
42 return 0;
43 }
44 return -1;
45 }
46
ParseBooteventCompareProc(ListNode * node,void * data)47 static int ParseBooteventCompareProc(ListNode *node, void *data)
48 {
49 BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)node;
50 if (strcmp(item->paramName, (const char *)data) == 0) {
51 return 0;
52 }
53 return -1;
54 }
55
AddServiceBootEvent(const char * serviceName,const char * paramName)56 static int AddServiceBootEvent(const char *serviceName, const char *paramName)
57 {
58 ServiceExtData *extData = NULL;
59 ListNode *found = NULL;
60 if (strncmp(paramName, BOOT_EVENT_PARA_PREFIX, BOOT_EVENT_PARA_PREFIX_LEN) != 0) {
61 return -1;
62 }
63 found = OH_ListFind(&bootEventList, (void *)paramName, ParseBooteventCompareProc);
64 if (found != NULL) {
65 return -1;
66 }
67 for (int i = HOOK_ID_BOOTEVENT; i < HOOK_ID_BOOTEVENT_MAX; i++) {
68 extData = AddServiceExtData(serviceName, i, NULL, sizeof(BOOT_EVENT_PARAM_ITEM));
69 if (extData != NULL) {
70 break;
71 }
72 }
73
74 INIT_CHECK(extData != NULL, return -1);
75
76 BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)extData->data;
77 OH_ListInit(&item->node);
78 for (int i = 0; i < BOOTEVENT_MAX; i++) {
79 item->timestamp[i].tv_nsec = 0;
80 item->timestamp[i].tv_sec = 0;
81 }
82 item->paramName = strdup(paramName);
83 if (item->paramName == NULL) {
84 DelServiceExtData(serviceName, extData->dataId);
85 INIT_LOGI("strdup failed");
86 return -1;
87 }
88 item->flags = BOOTEVENT_TYPE_SERVICE;
89 OH_ListAddTail(&bootEventList, (ListNode *)&item->node);
90 return 0;
91 }
92
AddInitBootEvent(const char * bootEventName)93 static void AddInitBootEvent(const char *bootEventName)
94 {
95 ListNode *found = NULL;
96 found = OH_ListFind(&bootEventList, (void *)bootEventName, ParseBooteventCompareProc);
97 if (found != NULL) {
98 (void)clock_gettime(CLOCK_MONOTONIC, &(((BOOT_EVENT_PARAM_ITEM *)found)->timestamp[BOOTEVENT_READY]));
99 return;
100 }
101
102 BOOT_EVENT_PARAM_ITEM *item = calloc(1, sizeof(BOOT_EVENT_PARAM_ITEM));
103 INIT_CHECK(item != NULL, return);
104
105 OH_ListInit(&item->node);
106
107 (void)clock_gettime(CLOCK_MONOTONIC, &(item->timestamp[BOOTEVENT_FORK]));
108
109 item->paramName = strdup(bootEventName);
110 INIT_CHECK(item->paramName != NULL, free(item);
111 return);
112
113 item->flags = BOOTEVENT_TYPE_JOB;
114 OH_ListAddTail(&bootEventList, (ListNode *)&item->node);
115 return;
116 }
117
118 #define BOOT_EVENT_BOOT_COMPLETED "bootevent.boot.completed"
119
BootEventDestroy(ListNode * node)120 static void BootEventDestroy(ListNode *node)
121 {
122 BOOT_EVENT_PARAM_ITEM *bootEvent = (BOOT_EVENT_PARAM_ITEM *)node;
123 INIT_CHECK(bootEvent->paramName == NULL, free((void *)bootEvent->paramName));
124 free((void *)bootEvent);
125 }
126
AddItemToJson(cJSON * root,const char * name,double startTime,int pid,double durTime)127 static int AddItemToJson(cJSON *root, const char *name, double startTime, int pid, double durTime)
128 {
129 cJSON *obj = cJSON_CreateObject(); // release obj at traverse done
130 INIT_CHECK_RETURN_VALUE(obj != NULL, -1);
131 cJSON_AddStringToObject(obj, "name", name);
132 cJSON_AddNumberToObject(obj, "ts", startTime);
133 cJSON_AddStringToObject(obj, "ph", "X");
134 cJSON_AddNumberToObject(obj, "pid", pid);
135 cJSON_AddNumberToObject(obj, "tid", pid);
136 cJSON_AddNumberToObject(obj, "dur", durTime);
137 cJSON_AddItemToArray(root, obj);
138 return 0;
139 }
140
BootEventTraversal(ListNode * node,void * root)141 static int BootEventTraversal(ListNode *node, void *root)
142 {
143 static int start = 0;
144 BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)node;
145 double forkTime = item->timestamp[BOOTEVENT_FORK].tv_sec * MSECTONSEC +
146 (double)item->timestamp[BOOTEVENT_FORK].tv_nsec / USTONSEC;
147 double readyTime = item->timestamp[BOOTEVENT_READY].tv_sec * MSECTONSEC +
148 (double)item->timestamp[BOOTEVENT_READY].tv_nsec / USTONSEC;
149 double durTime = readyTime - forkTime;
150 if (item->pid == 0) {
151 if (durTime < SAVEINITBOOTEVENTMSEC) {
152 return 0;
153 }
154 item->pid = 1; // 1 is init pid
155 }
156 if (start == 0) {
157 // set trace start time 0
158 INIT_CHECK_RETURN_VALUE(AddItemToJson((cJSON *)root, item->paramName, 0,
159 1, 0) == 0, -1);
160 start++;
161 }
162 INIT_CHECK_RETURN_VALUE(AddItemToJson((cJSON *)root, item->paramName, forkTime,
163 item->pid, durTime > 0 ? durTime : 0) == 0, -1);
164 return 0;
165 }
166
SaveServiceBootEvent()167 static int SaveServiceBootEvent()
168 {
169 INIT_CHECK(GetBootEventEnable(), return 0);
170 time_t nowTime = time(NULL);
171 INIT_CHECK_RETURN_VALUE(nowTime > 0, -1);
172 struct tm *p = localtime(&nowTime);
173 INIT_CHECK_RETURN_VALUE(p != NULL, -1);
174 char bootEventFileName[BOOT_EVENT_FILEPATH_MAX_LEN] = "";
175 INIT_CHECK_RETURN_VALUE(snprintf_s(bootEventFileName, BOOT_EVENT_FILEPATH_MAX_LEN, BOOT_EVENT_FILEPATH_MAX_LEN - 1,
176 BOOTEVENT_OUTPUT_PATH"%d%d%d-%d%d.bootevent",
177 1900 + p->tm_year, p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min) >= 0, -1); // 1900 is start year
178 CheckAndCreatFile(bootEventFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
179 FILE *tmpFile = fopen(bootEventFileName, "wr");
180 INIT_CHECK_RETURN_VALUE(tmpFile != NULL, -1);
181 cJSON *root = cJSON_CreateArray();
182 INIT_CHECK(root != NULL, (void)fclose(tmpFile);
183 return -1);
184
185 OH_ListTraversal(&bootEventList, (void *)root, BootEventTraversal, 0);
186 char *buff = cJSON_Print(root);
187 if (buff == NULL) {
188 cJSON_Delete(root);
189 (void)fclose(tmpFile);
190 return -1;
191 }
192 INIT_CHECK_ONLY_ELOG(fprintf(tmpFile, "%s\n", buff) >= 0, "save boot event file failed");
193 free(buff);
194 cJSON_Delete(root);
195 (void)fflush(tmpFile);
196 (void)fclose(tmpFile);
197 return 0;
198 }
199
ReportSysEvent(void)200 static void ReportSysEvent(void)
201 {
202 INIT_CHECK(GetBootEventEnable(), return);
203 #ifndef STARTUP_INIT_TEST
204 InitModuleMgrInstall("eventmodule");
205 InitModuleMgrUnInstall("eventmodule");
206 #endif
207 return;
208 }
209
SetServiceBooteventHookMgr(const char * name,int state)210 static void SetServiceBooteventHookMgr(const char *name, int state)
211 {
212 #ifndef STARTUP_INIT_TEST
213 SERVICE_BOOTEVENT_CTX context;
214 context.serviceName = name;
215 context.reserved = NULL;
216 context.state = state;
217 HookMgrExecute(GetBootStageHookMgr(), INIT_SERVICE_BOOTEVENT, (void*)(&context), NULL);
218 #endif
219 }
220
BootEventParaFireByName(const char * paramName)221 static void BootEventParaFireByName(const char *paramName)
222 {
223 ListNode *found = NULL;
224 char *bootEventValue = strrchr(paramName, '.');
225 INIT_CHECK(bootEventValue != NULL, return);
226
227 bootEventValue[0] = '\0';
228
229 found = OH_ListFind(&bootEventList, (void *)paramName, BootEventParaListCompareProc);
230 if (found == NULL) {
231 return;
232 }
233 int ret = ((BOOT_EVENT_PARAM_ITEM *)found)->timestamp[BOOTEVENT_READY].tv_sec;
234 INIT_CHECK(ret == 0, return);
235
236 INIT_CHECK_ONLY_RETURN(clock_gettime(CLOCK_MONOTONIC,
237 &(((BOOT_EVENT_PARAM_ITEM *)found)->timestamp[BOOTEVENT_READY])) == 0);
238 g_bootEventNum--;
239 SetServiceBooteventHookMgr(paramName, 2); // 2: bootevent service has ready
240 // Check if all boot event params are fired
241 if (g_bootEventNum > 0) {
242 return;
243 }
244 // All parameters are fired, set boot completed now ...
245 INIT_LOGI("All boot events are fired, boot complete now ...");
246 SystemWriteParam(BOOT_EVENT_BOOT_COMPLETED, "true");
247 g_bootEventEnable = BOOT_EVENT_FINISH;
248 SaveServiceBootEvent();
249 // report complete event
250 ReportSysEvent();
251 const char *clearBootEventArgv[] = {"bootevent"};
252 // clear servie extra data
253 PluginExecCmd("clear", ARRAY_LENGTH(clearBootEventArgv), clearBootEventArgv);
254 #ifndef STARTUP_INIT_TEST
255 HookMgrExecute(GetBootStageHookMgr(), INIT_BOOT_COMPLETE, NULL, NULL);
256 #endif
257 AutorunModuleMgrUnInstall("init_bootDetector");
258 return;
259 }
260
261 #define BOOT_EVENT_FIELD_NAME "bootevents"
ServiceParseBootEventHook(SERVICE_PARSE_CTX * serviceParseCtx)262 static void ServiceParseBootEventHook(SERVICE_PARSE_CTX *serviceParseCtx)
263 {
264 INIT_CHECK(g_bootEventEnable != 0, return);
265 int cnt;
266 cJSON *bootEvents = cJSON_GetObjectItem(serviceParseCtx->serviceNode, BOOT_EVENT_FIELD_NAME);
267
268 // No boot events in config file
269 if (bootEvents == NULL) {
270 return;
271 }
272 SERVICE_INFO_CTX ctx = {0};
273 ctx.serviceName = serviceParseCtx->serviceName;
274 HookMgrExecute(GetBootStageHookMgr(), INIT_SERVICE_CLEAR, (void *)&ctx, NULL);
275 // Single boot event in config file
276 if (!cJSON_IsArray(bootEvents)) {
277 if (AddServiceBootEvent(serviceParseCtx->serviceName,
278 cJSON_GetStringValue(bootEvents)) != 0) {
279 INIT_LOGI("Add service bootEvent failed %s", serviceParseCtx->serviceName);
280 return;
281 }
282 g_bootEventNum++;
283 SetServiceBooteventHookMgr(serviceParseCtx->serviceName, 1); // 1: bootevent service is starting
284 return;
285 }
286
287 // Multiple boot events in config file
288 cnt = cJSON_GetArraySize(bootEvents);
289 for (int i = 0; i < cnt; i++) {
290 cJSON *item = cJSON_GetArrayItem(bootEvents, i);
291 if (AddServiceBootEvent(serviceParseCtx->serviceName,
292 cJSON_GetStringValue(item)) != 0) {
293 INIT_LOGI("Add service bootEvent failed %s", serviceParseCtx->serviceName);
294 continue;
295 }
296 g_bootEventNum++;
297 SetServiceBooteventHookMgr(serviceParseCtx->serviceName, 1); // 1: bootevent service is starting
298 }
299 }
300
AddCmdBootEvent(int argc,const char ** argv)301 static void AddCmdBootEvent(int argc, const char **argv)
302 {
303 if (argc < 4) { // 4 is min args cmd boot event required
304 return;
305 }
306
307 BOOT_EVENT_PARAM_ITEM *item = calloc(1, sizeof(BOOT_EVENT_PARAM_ITEM));
308 INIT_CHECK(item != NULL, return);
309 OH_ListInit(&item->node);
310 item->timestamp[BOOTEVENT_FORK] = ((INIT_TIMING_STAT *)argv[3])->startTime; // 3 args
311 item->timestamp[BOOTEVENT_READY] = ((INIT_TIMING_STAT *)argv[3])->endTime; // 3 args
312 int cmdLen = strlen(argv[1]) + strlen(argv[2]) + 1; // 2 args 1 '\0'
313 item->paramName = calloc(1, cmdLen);
314 if (item->paramName == NULL) {
315 free(item);
316 return;
317 }
318 for (int i = 1; i < 3; i++) { // 3 cmd content end
319 INIT_CHECK_ONLY_ELOG(strcat_s(item->paramName, cmdLen, argv[i]) >= 0, "combine cmd args failed");
320 }
321 item->flags = BOOTEVENT_TYPE_CMD;
322 OH_ListAddTail(&bootEventList, (ListNode *)&item->node);
323 return;
324 }
325
DoBootEventCmd(int id,const char * name,int argc,const char ** argv)326 static int DoBootEventCmd(int id, const char *name, int argc, const char **argv)
327 {
328 if (g_bootEventEnable == BOOT_EVENT_FINISH) {
329 return 0;
330 }
331 // clear init boot events that recorded before persist param read
332 if (g_bootEventEnable == 0) {
333 const char *clearBootEventArgv[] = {"bootevent"};
334 PluginExecCmd("clear", ARRAY_LENGTH(clearBootEventArgv), clearBootEventArgv);
335 OH_ListRemoveAll(&bootEventList, BootEventDestroy);
336 g_bootEventEnable = BOOT_EVENT_FINISH;
337 return 0;
338 }
339
340 PLUGIN_CHECK(argc >= 1, return -1, "Invalid parameter");
341 if (strcmp(argv[0], "init") == 0) {
342 if (argc < 2) { // 2 args
343 return 0;
344 }
345 AddInitBootEvent(argv[1]);
346 } else if (strcmp(argv[0], "cmd") == 0) {
347 AddCmdBootEvent(argc, argv);
348 } else {
349 // argv[0] samgr.ready.true
350 BootEventParaFireByName(argv[0]);
351 }
352 return 0;
353 }
354
355 static int32_t g_executorId = -1;
ParamSetBootEventHook(const HOOK_INFO * hookInfo,void * cookie)356 static int ParamSetBootEventHook(const HOOK_INFO *hookInfo, void *cookie)
357 {
358 if (g_executorId == -1) {
359 g_executorId = AddCmdExecutor("bootevent", DoBootEventCmd);
360 }
361 return 0;
362 }
363
ClearServiceBootEvent(SERVICE_INFO_CTX * serviceCtx)364 static void ClearServiceBootEvent(SERVICE_INFO_CTX *serviceCtx)
365 {
366 if (serviceCtx->reserved == NULL || strcmp(serviceCtx->reserved, "bootevent") == 0) {
367 for (int i = HOOK_ID_BOOTEVENT; i < HOOK_ID_BOOTEVENT_MAX; i++) {
368 ServiceExtData *extData = GetServiceExtData(serviceCtx->serviceName, i);
369 if (extData == NULL) {
370 return;
371 }
372 free(((BOOT_EVENT_PARAM_ITEM *)extData->data)->paramName);
373 OH_ListRemove(&((BOOT_EVENT_PARAM_ITEM *)extData->data)->node);
374 DelServiceExtData(serviceCtx->serviceName, i);
375 g_bootEventNum--;
376 }
377 // clear service extra data first
378 return;
379 }
380 if (strcmp(serviceCtx->reserved, "clearInitBootevent") == 0) {
381 // clear init boot event
382 OH_ListRemoveAll(&bootEventList, BootEventDestroy);
383 g_bootEventNum = 0;
384 }
385 return;
386 }
387
SetServiceBootEventFork(SERVICE_INFO_CTX * serviceCtx)388 static void SetServiceBootEventFork(SERVICE_INFO_CTX *serviceCtx)
389 {
390 INIT_CHECK(g_bootEventEnable != 0, return);
391 for (int i = HOOK_ID_BOOTEVENT; i < HOOK_ID_BOOTEVENT_MAX; i++) {
392 ServiceExtData *extData = GetServiceExtData(serviceCtx->serviceName, i);
393 if (extData == NULL) {
394 return;
395 }
396 if (serviceCtx->reserved != NULL) {
397 ((BOOT_EVENT_PARAM_ITEM *)extData->data)->pid = *((int *)serviceCtx->reserved);
398 continue;
399 }
400 INIT_CHECK_ONLY_RETURN(clock_gettime(CLOCK_MONOTONIC,
401 &(((BOOT_EVENT_PARAM_ITEM *)extData->data)->timestamp[BOOTEVENT_FORK])) == 0);
402 }
403 return;
404 }
405
GetBootEventEnable(void)406 int GetBootEventEnable(void)
407 {
408 char bootEventOpen[6] = ""; // 6 is length of bool value
409 uint32_t len = sizeof(bootEventOpen);
410 SystemReadParam("persist.init.bootevent.enable", bootEventOpen, &len);
411 if (strcmp(bootEventOpen, "true") == 0 || strcmp(bootEventOpen, "1") == 0) {
412 return 1;
413 }
414 return 0;
415 }
416
GetBootEventList(void)417 ListNode *GetBootEventList(void)
418 {
419 return &bootEventList;
420 }
421
RecordInitCmd(const HOOK_INFO * info,void * cookie)422 static int RecordInitCmd(const HOOK_INFO *info, void *cookie)
423 {
424 INIT_CMD_INFO *cmdCtx = (INIT_CMD_INFO *)cookie;
425 const char *bootEventArgv[] = {"cmd", cmdCtx->cmdName, cmdCtx->cmdContent, cmdCtx->reserved};
426 return DoBootEventCmd(0, NULL, ARRAY_LENGTH(bootEventArgv), bootEventArgv);
427 }
428
MODULE_CONSTRUCTOR(void)429 MODULE_CONSTRUCTOR(void)
430 {
431 HOOK_INFO info = {INIT_CMD_RECORD, 0, RecordInitCmd, NULL};
432 HookMgrAddEx(GetBootStageHookMgr(), &info);
433 InitAddServiceHook(SetServiceBootEventFork, INIT_SERVICE_FORK_BEFORE);
434 InitAddServiceHook(SetServiceBootEventFork, INIT_SERVICE_FORK_AFTER);
435 InitAddClearServiceHook(ClearServiceBootEvent);
436 InitAddServiceParseHook(ServiceParseBootEventHook);
437 InitAddGlobalInitHook(0, ParamSetBootEventHook);
438 }
439