• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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