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