1 /*
2 * Copyright (c) 2021-2024 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_group_manager.h"
20 #include "init_cmdexecutor.h"
21 #include "trigger_manager.h"
22 #include "init_log.h"
23 #include "plugin_adapter.h"
24 #include "init_hook.h"
25 #include "init_service.h"
26 #include "bootstage.h"
27 #include "securec.h"
28 #include "init_utils.h"
29 #include "init_cmds.h"
30 #include "config_policy_utils.h"
31
32 #ifdef WITH_SELINUX
33 #include <policycoreutils.h>
34 #endif
35 #ifndef STARTUP_INIT_TEST
36 static const int SLEPP_TIME = 100;
37 #endif
38
GetBootSwitchEnable(const char * paramName)39 static int GetBootSwitchEnable(const char *paramName)
40 {
41 char bootEventOpen[6] = ""; // 6 is length of bool value
42 uint32_t len = sizeof(bootEventOpen);
43 SystemReadParam(paramName, bootEventOpen, &len);
44 if (strcmp(bootEventOpen, "true") == 0 || strcmp(bootEventOpen, "1") == 0) {
45 return 1;
46 }
47 return 0;
48 }
49
50 static int g_bootEventNum = 0;
51
52 static ListNode bootEventList = {&bootEventList, &bootEventList};
53
BootEventParaListCompareProc(ListNode * node,void * data)54 static int BootEventParaListCompareProc(ListNode *node, void *data)
55 {
56 BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)node;
57 if (strncmp(item->paramName, BOOT_EVENT_PARA_PREFIX, BOOT_EVENT_PARA_PREFIX_LEN) != 0) {
58 return -1;
59 }
60 if (strcmp(item->paramName + BOOT_EVENT_PARA_PREFIX_LEN, (const char *)data) == 0) {
61 return 0;
62 }
63 return -1;
64 }
65
ParseBooteventCompareProc(ListNode * node,void * data)66 static int ParseBooteventCompareProc(ListNode *node, void *data)
67 {
68 BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)node;
69 if (strcmp(item->paramName, (const char *)data) == 0) {
70 return 0;
71 }
72 return -1;
73 }
74
AddBootEventItem(BOOT_EVENT_PARAM_ITEM * item,const char * paramName)75 static int AddBootEventItem(BOOT_EVENT_PARAM_ITEM *item, const char *paramName)
76 {
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 free(item);
85 return -1;
86 }
87 item->flags = BOOTEVENT_TYPE_SERVICE;
88 OH_ListAddTail(&bootEventList, (ListNode *)&item->node);
89 g_bootEventNum++;
90 return 0;
91 }
92
AddBootEventItemByName(const char * paramName)93 static int AddBootEventItemByName(const char *paramName)
94 {
95 BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)calloc(1, sizeof(BOOT_EVENT_PARAM_ITEM));
96 if (item == NULL) {
97 return -1;
98 }
99
100 return AddBootEventItem(item, paramName);
101 }
102
SetServiceBooteventHookMgr(const char * serviceName,const char * paramName,int state)103 static void SetServiceBooteventHookMgr(const char *serviceName, const char *paramName, int state)
104 {
105 #ifndef STARTUP_INIT_TEST
106 SERVICE_BOOTEVENT_CTX context;
107 context.serviceName = serviceName;
108 context.reserved = paramName;
109 context.state = state;
110 HookMgrExecute(GetBootStageHookMgr(), INIT_SERVICE_BOOTEVENT, (void*)(&context), NULL);
111 #endif
112 }
113
114
AddServiceBootEvent(const char * serviceName,const char * paramName)115 static int AddServiceBootEvent(const char *serviceName, const char *paramName)
116 {
117 ServiceExtData *extData = NULL;
118 ListNode *found = NULL;
119 if ((paramName == NULL) || (strncmp(paramName, BOOT_EVENT_PARA_PREFIX, BOOT_EVENT_PARA_PREFIX_LEN) != 0)) {
120 return -1;
121 }
122 found = OH_ListFind(&bootEventList, (void *)paramName, ParseBooteventCompareProc);
123 if (found != NULL) {
124 return -1;
125 }
126 // Find an empty bootevent data position
127 for (int i = HOOK_ID_BOOTEVENT; i < HOOK_ID_BOOTEVENT_MAX; i++) {
128 extData = AddServiceExtData(serviceName, i, NULL, sizeof(BOOT_EVENT_PARAM_ITEM));
129 if (extData != NULL) {
130 break;
131 }
132 }
133
134 INIT_CHECK(extData != NULL, return -1);
135
136 BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)extData->data;
137
138 if (AddBootEventItem(item, paramName) != 0) {
139 DelServiceExtData(serviceName, extData->dataId);
140 return -1;
141 }
142
143 SetServiceBooteventHookMgr(serviceName, paramName, 1);
144 return 0;
145 }
146
AddInitBootEvent(const char * bootEventName)147 static void AddInitBootEvent(const char *bootEventName)
148 {
149 BOOT_EVENT_PARAM_ITEM *found = NULL;
150 found = (BOOT_EVENT_PARAM_ITEM *)OH_ListFind(&bootEventList, (void *)bootEventName, ParseBooteventCompareProc);
151 if (found != NULL) {
152 (void)clock_gettime(CLOCK_MONOTONIC, &(found->timestamp[BOOTEVENT_READY]));
153 return;
154 }
155
156 BOOT_EVENT_PARAM_ITEM *item = calloc(1, sizeof(BOOT_EVENT_PARAM_ITEM));
157 INIT_CHECK(item != NULL, return);
158
159 OH_ListInit(&item->node);
160
161 (void)clock_gettime(CLOCK_MONOTONIC, &(item->timestamp[BOOTEVENT_FORK]));
162
163 item->paramName = strdup(bootEventName);
164 INIT_CHECK(item->paramName != NULL, free(item);
165 return);
166
167 item->flags = BOOTEVENT_TYPE_JOB;
168 OH_ListAddTail(&bootEventList, (ListNode *)&item->node);
169 return;
170 }
171
172 #define BOOT_EVENT_BOOT_COMPLETED "bootevent.boot.completed"
173
BootEventDestroy(ListNode * node)174 static void BootEventDestroy(ListNode *node)
175 {
176 BOOT_EVENT_PARAM_ITEM *bootEvent = (BOOT_EVENT_PARAM_ITEM *)node;
177 INIT_CHECK(bootEvent->paramName == NULL, free((void *)bootEvent->paramName));
178 free((void *)bootEvent);
179 }
180
AddItemToJson(cJSON * root,const char * name,double startTime,int pid,double durTime)181 static int AddItemToJson(cJSON *root, const char *name, double startTime, int pid, double durTime)
182 {
183 cJSON *obj = cJSON_CreateObject(); // release obj at traverse done
184 INIT_CHECK_RETURN_VALUE(obj != NULL, -1);
185 cJSON_AddStringToObject(obj, "name", name);
186 cJSON_AddNumberToObject(obj, "ts", startTime);
187 cJSON_AddStringToObject(obj, "ph", "X");
188 cJSON_AddNumberToObject(obj, "pid", pid);
189 cJSON_AddNumberToObject(obj, "tid", pid);
190 cJSON_AddNumberToObject(obj, "dur", durTime);
191 cJSON_AddItemToArray(root, obj);
192 return 0;
193 }
194
BootEventTraversal(ListNode * node,void * root)195 static int BootEventTraversal(ListNode *node, void *root)
196 {
197 static int start = 0;
198 BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)node;
199 double forkTime = (double)item->timestamp[BOOTEVENT_FORK].tv_sec * MSECTONSEC +
200 (double)item->timestamp[BOOTEVENT_FORK].tv_nsec / USTONSEC;
201 double readyTime = (double)item->timestamp[BOOTEVENT_READY].tv_sec * MSECTONSEC +
202 (double)item->timestamp[BOOTEVENT_READY].tv_nsec / USTONSEC;
203 double durTime = readyTime - forkTime;
204 if (item->pid == 0) {
205 if (durTime < SAVEINITBOOTEVENTMSEC) {
206 return 0;
207 }
208 item->pid = 1; // 1 is init pid
209 }
210 if (start == 0) {
211 // set trace start time 0
212 INIT_CHECK_RETURN_VALUE(AddItemToJson((cJSON *)root, item->paramName, 0,
213 1, 0) == 0, -1);
214 start++;
215 }
216 INIT_CHECK_RETURN_VALUE(AddItemToJson((cJSON *)root, item->paramName, forkTime,
217 item->pid, durTime > 0 ? durTime : 0) == 0, -1);
218 return 0;
219 }
220
CreateBootEventFile(const char * file,mode_t mode)221 static int CreateBootEventFile(const char *file, mode_t mode)
222 {
223 if (access(file, F_OK) == 0) {
224 INIT_LOGW("File %s already exist", file);
225 return 0;
226 }
227 if (errno != ENOENT) {
228 INIT_LOGW("Failed to access %s, err = %d", file, errno);
229 return -1;
230 }
231 CheckAndCreateDir(file);
232 int fd = open(file, O_CREAT, mode);
233 if (fd < 0) {
234 INIT_LOGE("Failed create %s, err=%d", file, errno);
235 return -1;
236 }
237 close(fd);
238 #ifdef WITH_SELINUX
239 INIT_LOGI("start to restorecon selinux");
240 (void)RestoreconRecurse(BOOTEVENT_OUTPUT_PATH);
241 #endif
242 return 0;
243 }
244
SaveServiceBootEvent()245 static int SaveServiceBootEvent()
246 {
247 INIT_CHECK(GetBootSwitchEnable("persist.init.bootuptrace.enable"), return 0);
248
249 int ret = CreateBootEventFile(BOOTEVENT_OUTPUT_PATH "bootup.trace", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
250 INIT_CHECK_RETURN_VALUE(ret == 0, -1);
251 FILE *tmpFile = fopen(BOOTEVENT_OUTPUT_PATH "bootup.trace", "wr");
252 INIT_CHECK_RETURN_VALUE(tmpFile != NULL, -1);
253 cJSON *root = cJSON_CreateArray();
254 INIT_CHECK(root != NULL, (void)fclose(tmpFile);
255 return -1);
256
257 OH_ListTraversal(&bootEventList, (void *)root, BootEventTraversal, 0);
258 char *buff = cJSON_Print(root);
259 if (buff == NULL) {
260 cJSON_Delete(root);
261 (void)fclose(tmpFile);
262 return -1;
263 }
264 INIT_CHECK_ONLY_ELOG(fprintf(tmpFile, "%s\n", buff) >= 0, "save boot event file failed");
265 free(buff);
266 cJSON_Delete(root);
267 (void)fflush(tmpFile);
268 (void)fclose(tmpFile);
269 return 0;
270 }
271
ReportSysEvent(void)272 static void ReportSysEvent(void)
273 {
274 INIT_CHECK(GetBootSwitchEnable("persist.init.bootevent.enable"), return);
275 #ifndef STARTUP_INIT_TEST
276 InitModuleMgrInstall("eventmodule");
277 InitModuleMgrUnInstall("eventmodule");
278 #endif
279 return;
280 }
281
BootCompleteClearAll(void)282 static void BootCompleteClearAll(void)
283 {
284 InitGroupNode *node = GetNextGroupNode(NODE_TYPE_SERVICES, NULL);
285 while (node != NULL) {
286 if (node->data.service == NULL) {
287 node = GetNextGroupNode(NODE_TYPE_SERVICES, node);
288 continue;
289 }
290 for (int i = HOOK_ID_BOOTEVENT; i < HOOK_ID_BOOTEVENT_MAX; i++) {
291 ServiceExtData *extData = GetServiceExtData(node->name, i);
292 if (extData == NULL) {
293 return;
294 }
295 free(((BOOT_EVENT_PARAM_ITEM *)extData->data)->paramName);
296 OH_ListRemove(&((BOOT_EVENT_PARAM_ITEM *)extData->data)->node);
297 DelServiceExtData(node->name, i);
298 }
299 }
300
301 // clear init boot event
302 OH_ListRemoveAll(&bootEventList, BootEventDestroy);
303 g_bootEventNum = 0;
304 }
305
WriteBooteventSysParam(const char * paramName)306 static void WriteBooteventSysParam(const char *paramName)
307 {
308 char buf[64];
309 long long uptime;
310 char name[PARAM_NAME_LEN_MAX];
311
312 uptime = GetUptimeInMicroSeconds(NULL);
313
314 INIT_CHECK_ONLY_ELOG(snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%lld", uptime) >= 0,
315 "snprintf_s buf failed");
316 INIT_CHECK_ONLY_ELOG(snprintf_s(name, sizeof(name), sizeof(name) - 1, "ohos.boot.time.%s", paramName) >= 0,
317 "snprintf_s name failed");
318 SystemWriteParam(name, buf);
319 }
320
321 #ifndef STARTUP_INIT_TEST
DelayedHookMgrExecute(TimerHandle handler,void * context)322 static void DelayedHookMgrExecute(TimerHandle handler, void *context)
323 {
324 UNUSED(context);
325 INIT_LOGI("Executing delayed HookMgrExecute after sleep");
326 HookMgrExecute(GetBootStageHookMgr(), INIT_BOOT_COMPLETE, NULL, NULL);
327 LE_StopTimer(LE_GetDefaultLoop(), handler);
328 }
329
ScheduleDelayedHookMgrExecute(void)330 static int ScheduleDelayedHookMgrExecute(void)
331 {
332 TimerHandle timer;
333 LE_STATUS status = LE_CreateTimer(LE_GetDefaultLoop(), &timer, DelayedHookMgrExecute, NULL);
334 if (status != LE_SUCCESS) {
335 INIT_LOGE("Failed to create timer for delayed HookMgrExecute");
336 return -1;
337 }
338
339 status = LE_StartTimer(LE_GetDefaultLoop(), timer, SLEPP_TIME, 0);
340 if (status != LE_SUCCESS) {
341 LE_StopTimer(LE_GetDefaultLoop(), timer);
342 INIT_LOGE("Failed to start timer for delayed HookMgrExecute");
343 return -1;
344 }
345 return 0;
346 }
347 #endif
348
349
BootEventParaFireByName(const char * paramName)350 static int BootEventParaFireByName(const char *paramName)
351 {
352 BOOT_EVENT_PARAM_ITEM *found = NULL;
353
354 char *bootEventValue = strrchr(paramName, '.');
355 INIT_CHECK(bootEventValue != NULL, return 0);
356 bootEventValue[0] = '\0';
357
358 WriteBooteventSysParam(paramName);
359
360 found = (BOOT_EVENT_PARAM_ITEM *)OH_ListFind(&bootEventList, (void *)paramName, BootEventParaListCompareProc);
361 if (found == NULL) {
362 return 0;
363 }
364
365 // Already fired
366 if (found->timestamp[BOOTEVENT_READY].tv_sec > 0) {
367 return 0;
368 }
369 INIT_CHECK_RETURN_VALUE(clock_gettime(CLOCK_MONOTONIC,
370 &(found->timestamp[BOOTEVENT_READY])) == 0, 0);
371
372 g_bootEventNum--;
373 SetServiceBooteventHookMgr(NULL, paramName, 2); // 2: bootevent service has ready
374 // Check if all boot event params are fired
375 if (g_bootEventNum > 0) {
376 return 0;
377 }
378 // All parameters are fired, set boot completed now ...
379 INIT_LOGI("All boot events are fired, boot complete now ...");
380 SystemWriteParam(BOOT_EVENT_BOOT_COMPLETED, "true");
381 SetBootCompleted(true);
382 SaveServiceBootEvent();
383 // report complete event
384 ReportSysEvent();
385 BootCompleteClearAll();
386 #ifndef STARTUP_INIT_TEST
387 if (ScheduleDelayedHookMgrExecute() != 0) {
388 INIT_LOGE("Failed to schedule delayed HookMgrExecute, executing directly");
389 HookMgrExecute(GetBootStageHookMgr(), INIT_BOOT_COMPLETE, NULL, NULL);
390 }
391 #endif
392 RemoveCmdExecutor("bootevent", -1);
393 return 1;
394 }
395
396 #define BOOT_EVENT_FIELD_NAME "bootevents"
ServiceParseBootEventHook(SERVICE_PARSE_CTX * serviceParseCtx)397 static void ServiceParseBootEventHook(SERVICE_PARSE_CTX *serviceParseCtx)
398 {
399 int cnt;
400 cJSON *bootEvents = cJSON_GetObjectItem(serviceParseCtx->serviceNode, BOOT_EVENT_FIELD_NAME);
401
402 // No boot events in config file
403 if (bootEvents == NULL) {
404 return;
405 }
406 SERVICE_INFO_CTX ctx = {0};
407 ctx.serviceName = serviceParseCtx->serviceName;
408 HookMgrExecute(GetBootStageHookMgr(), INIT_SERVICE_CLEAR, (void *)&ctx, NULL);
409 // Single boot event in config file
410 if (!cJSON_IsArray(bootEvents)) {
411 if (AddServiceBootEvent(serviceParseCtx->serviceName,
412 cJSON_GetStringValue(bootEvents)) != 0) {
413 INIT_LOGI("Add service bootEvent failed %s", serviceParseCtx->serviceName);
414 return;
415 }
416 return;
417 }
418
419 // Multiple boot events in config file
420 cnt = cJSON_GetArraySize(bootEvents);
421 for (int i = 0; i < cnt; i++) {
422 cJSON *item = cJSON_GetArrayItem(bootEvents, i);
423 if (AddServiceBootEvent(serviceParseCtx->serviceName,
424 cJSON_GetStringValue(item)) != 0) {
425 INIT_LOGI("Add service bootEvent failed %s", serviceParseCtx->serviceName);
426 continue;
427 }
428 }
429 }
430
431 static int g_finished = 0;
DoBootEventCmd(int id,const char * name,int argc,const char ** argv)432 static int DoBootEventCmd(int id, const char *name, int argc, const char **argv)
433 {
434 if (g_finished) {
435 return 0;
436 }
437
438 PLUGIN_CHECK(argc >= 1, return -1, "Invalid parameter");
439 if (strcmp(argv[0], "init") == 0) {
440 if (argc < 2) { // 2 args
441 return 0;
442 }
443 AddInitBootEvent(argv[1]);
444 } else {
445 // argv[0] samgr.ready.true
446 g_finished = BootEventParaFireByName(argv[0]);
447 }
448 return 0;
449 }
450
AddReservedBooteventsByFile(const char * name)451 static void AddReservedBooteventsByFile(const char *name)
452 {
453 char buf[MAX_PATH_LEN];
454
455 FILE *file = fopen(name, "r");
456 if (file == NULL) {
457 return;
458 }
459
460 while (fgets((void *)buf, sizeof(buf) - 1, file)) {
461 buf[sizeof(buf) - 1] = '\0';
462 char *end = strchr(buf, '\r');
463 if (end != NULL) {
464 *end = '\0';
465 }
466 end = strchr(buf, '\n');
467 if (end != NULL) {
468 *end = '\0';
469 }
470 INIT_LOGI("Got priv-app bootevent: %s", buf);
471 AddBootEventItemByName(buf);
472 }
473 (void)fclose(file);
474 }
475
AddReservedBootevents(void)476 static void AddReservedBootevents(void)
477 {
478 CfgFiles *files = GetCfgFiles("etc/init/priv_app.bootevents");
479 for (int i = MAX_CFG_POLICY_DIRS_CNT - 1; files && i >= 0; i--) {
480 if (files->paths[i]) {
481 AddReservedBooteventsByFile(files->paths[i]);
482 }
483 }
484 FreeCfgFiles(files);
485 }
486
DoUnsetBootEventCmd(int id,const char * name,int argc,const char ** argv)487 static int DoUnsetBootEventCmd(int id, const char *name, int argc, const char **argv)
488 {
489 if ((argc < 1) || (argv[0] == NULL) || (strlen(argv[0]) <= strlen(BOOT_EVENT_PARA_PREFIX)) ||
490 (strncmp(argv[0], BOOT_EVENT_PARA_PREFIX, strlen(BOOT_EVENT_PARA_PREFIX)) != 0)) {
491 return INIT_EPARAMETER;
492 }
493 const char *eventName = argv[0] + strlen(BOOT_EVENT_PARA_PREFIX);
494 BOOT_EVENT_PARAM_ITEM *item =
495 (BOOT_EVENT_PARAM_ITEM *)OH_ListFind(&bootEventList, (void *)eventName, BootEventParaListCompareProc);
496 PLUGIN_CHECK(item != NULL, return INIT_EPARAMETER, "item NULL");
497
498 if (item->timestamp[BOOTEVENT_READY].tv_sec == 0) {
499 INIT_LOGW("%s not set", argv[0]);
500 return INIT_OK;
501 }
502
503 SystemWriteParam(argv[0], "false");
504 if (g_finished != 0) {
505 SystemWriteParam(BOOT_EVENT_BOOT_COMPLETED, "false");
506 SetBootCompleted(false);
507 g_finished = 0;
508 }
509
510 item->timestamp[BOOTEVENT_READY].tv_sec = 0;
511 g_bootEventNum++;
512 INIT_LOGI("UnsetBootEvent %s g_bootEventNum:%d", argv[0], g_bootEventNum);
513 return INIT_OK;
514 }
515
ParamSetBootEventHook(const HOOK_INFO * hookInfo,void * cookie)516 static int ParamSetBootEventHook(const HOOK_INFO *hookInfo, void *cookie)
517 {
518 AddReservedBootevents();
519 AddCmdExecutor("bootevent", DoBootEventCmd);
520 AddCmdExecutor("unset_bootevent", DoUnsetBootEventCmd);
521 return 0;
522 }
523
SetServiceBootEventFork(SERVICE_INFO_CTX * serviceCtx)524 static void SetServiceBootEventFork(SERVICE_INFO_CTX *serviceCtx)
525 {
526 BOOT_EVENT_PARAM_ITEM *item;
527 for (int i = HOOK_ID_BOOTEVENT; i < HOOK_ID_BOOTEVENT_MAX; i++) {
528 ServiceExtData *extData = GetServiceExtData(serviceCtx->serviceName, i);
529 if (extData == NULL) {
530 return;
531 }
532 item = (BOOT_EVENT_PARAM_ITEM *)extData->data;
533 if (serviceCtx->reserved != NULL) {
534 item->pid = *((int *)serviceCtx->reserved);
535 }
536 INIT_CHECK_ONLY_RETURN(clock_gettime(CLOCK_MONOTONIC,
537 &(item->timestamp[BOOTEVENT_FORK])) == 0);
538 }
539 }
540
GetBootEventList(void)541 ListNode *GetBootEventList(void)
542 {
543 return &bootEventList;
544 }
545
AddCmdBootEvent(INIT_CMD_INFO * cmdCtx)546 static void AddCmdBootEvent(INIT_CMD_INFO *cmdCtx)
547 {
548 INIT_TIMING_STAT *timeStat = (INIT_TIMING_STAT *)cmdCtx->reserved;
549 long long diff = InitDiffTime(timeStat);
550 // If not time cost, just ignore
551 if (diff < SAVEINITBOOTEVENTMSEC) {
552 return;
553 }
554 BOOT_EVENT_PARAM_ITEM *item = calloc(1, sizeof(BOOT_EVENT_PARAM_ITEM));
555 if (item == NULL) {
556 return;
557 }
558 OH_ListInit(&item->node);
559 item->timestamp[BOOTEVENT_FORK] = timeStat->startTime;
560 item->timestamp[BOOTEVENT_READY] = timeStat->endTime;
561 int cmdLen = strlen(cmdCtx->cmdName) + strlen(cmdCtx->cmdContent) + 1; // 2 args 1 '\0'
562 item->paramName = calloc(1, cmdLen);
563 if (item->paramName == NULL) {
564 free(item);
565 return;
566 }
567 INIT_CHECK_ONLY_ELOG(snprintf_s(item->paramName, cmdLen, cmdLen - 1, "%s%s",
568 cmdCtx->cmdName, cmdCtx->cmdContent) >= 0,
569 "combine cmd args failed");
570 item->flags = BOOTEVENT_TYPE_CMD;
571 OH_ListAddTail(&bootEventList, (ListNode *)&item->node);
572 }
573
RecordInitCmd(const HOOK_INFO * info,void * cookie)574 static int RecordInitCmd(const HOOK_INFO *info, void *cookie)
575 {
576 if (cookie == NULL) {
577 return 0;
578 }
579 AddCmdBootEvent((INIT_CMD_INFO *)cookie);
580 return 0;
581 }
582
MODULE_CONSTRUCTOR(void)583 MODULE_CONSTRUCTOR(void)
584 {
585 // Add hook to record time-cost commands
586 HOOK_INFO info = {INIT_CMD_RECORD, 0, RecordInitCmd, NULL};
587 HookMgrAddEx(GetBootStageHookMgr(), &info);
588
589 // Add hook to parse all services with bootevents
590 InitAddServiceParseHook(ServiceParseBootEventHook);
591
592 // Add hook to record start time for services with bootevents
593 InitAddServiceHook(SetServiceBootEventFork, INIT_SERVICE_FORK_AFTER);
594
595 InitAddGlobalInitHook(0, ParamSetBootEventHook);
596 }
597