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