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