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