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_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
DoBootEventCmd(int id,const char * name,int argc,const char ** argv)371 static int DoBootEventCmd(int id, const char *name, int argc, const char **argv)
372 {
373 static int finished = 0;
374
375 if (finished) {
376 return 0;
377 }
378
379 PLUGIN_CHECK(argc >= 1, return -1, "Invalid parameter");
380 if (strcmp(argv[0], "init") == 0) {
381 if (argc < 2) { // 2 args
382 return 0;
383 }
384 AddInitBootEvent(argv[1]);
385 } else {
386 // argv[0] samgr.ready.true
387 finished = BootEventParaFireByName(argv[0]);
388 }
389 return 0;
390 }
391
AddReservedBooteventsByFile(const char * name)392 static void AddReservedBooteventsByFile(const char *name)
393 {
394 char buf[MAX_PATH_LEN];
395
396 FILE *file = fopen(name, "r");
397 if (file == NULL) {
398 return;
399 }
400
401 while (fgets((void *)buf, sizeof(buf) - 1, file)) {
402 buf[sizeof(buf) - 1] = '\0';
403 char *end = strchr(buf, '\r');
404 if (end != NULL) {
405 *end = '\0';
406 }
407 end = strchr(buf, '\n');
408 if (end != NULL) {
409 *end = '\0';
410 }
411 INIT_LOGI("Got priv-app bootevent: %s", buf);
412 AddBootEventItemByName(buf);
413 }
414 fclose(file);
415 }
416
AddReservedBootevents(void)417 static void AddReservedBootevents(void) {
418 CfgFiles *files = GetCfgFiles("etc/init/priv_app.bootevents");
419 for (int i = MAX_CFG_POLICY_DIRS_CNT - 1; files && i >= 0; i--) {
420 if (files->paths[i]) {
421 AddReservedBooteventsByFile(files->paths[i]);
422 }
423 }
424 FreeCfgFiles(files);
425 }
426
ParamSetBootEventHook(const HOOK_INFO * hookInfo,void * cookie)427 static int ParamSetBootEventHook(const HOOK_INFO *hookInfo, void *cookie)
428 {
429 AddReservedBootevents();
430 AddCmdExecutor("bootevent", DoBootEventCmd);
431 return 0;
432 }
433
SetServiceBootEventFork(SERVICE_INFO_CTX * serviceCtx)434 static void SetServiceBootEventFork(SERVICE_INFO_CTX *serviceCtx)
435 {
436 BOOT_EVENT_PARAM_ITEM *item;
437 for (int i = HOOK_ID_BOOTEVENT; i < HOOK_ID_BOOTEVENT_MAX; i++) {
438 ServiceExtData *extData = GetServiceExtData(serviceCtx->serviceName, i);
439 if (extData == NULL) {
440 return;
441 }
442 item = (BOOT_EVENT_PARAM_ITEM *)extData->data;
443 if (serviceCtx->reserved != NULL) {
444 item->pid = *((int *)serviceCtx->reserved);
445 }
446 INIT_CHECK_ONLY_RETURN(clock_gettime(CLOCK_MONOTONIC,
447 &(item->timestamp[BOOTEVENT_FORK])) == 0);
448 }
449 }
450
GetBootEventList(void)451 ListNode *GetBootEventList(void)
452 {
453 return &bootEventList;
454 }
455
AddCmdBootEvent(INIT_CMD_INFO * cmdCtx)456 static void AddCmdBootEvent(INIT_CMD_INFO *cmdCtx)
457 {
458 INIT_TIMING_STAT *timeStat = (INIT_TIMING_STAT *)cmdCtx->reserved;
459 long long diff = InitDiffTime(timeStat);
460 // If not time cost, just ignore
461 if (diff < SAVEINITBOOTEVENTMSEC) {
462 return;
463 }
464 BOOT_EVENT_PARAM_ITEM *item = calloc(1, sizeof(BOOT_EVENT_PARAM_ITEM));
465 if (item == NULL) {
466 return;
467 }
468 OH_ListInit(&item->node);
469 item->timestamp[BOOTEVENT_FORK] = timeStat->startTime;
470 item->timestamp[BOOTEVENT_READY] = timeStat->endTime;
471 int cmdLen = strlen(cmdCtx->cmdName) + strlen(cmdCtx->cmdContent) + 1; // 2 args 1 '\0'
472 item->paramName = calloc(1, cmdLen);
473 if (item->paramName == NULL) {
474 free(item);
475 return;
476 }
477 INIT_CHECK_ONLY_ELOG(snprintf_s(item->paramName, cmdLen, cmdLen - 1, "%s%s",
478 cmdCtx->cmdName, cmdCtx->cmdContent) >= 0,
479 "combine cmd args failed");
480 item->flags = BOOTEVENT_TYPE_CMD;
481 OH_ListAddTail(&bootEventList, (ListNode *)&item->node);
482 }
483
RecordInitCmd(const HOOK_INFO * info,void * cookie)484 static int RecordInitCmd(const HOOK_INFO *info, void *cookie)
485 {
486 if (cookie == NULL) {
487 return 0;
488 }
489 AddCmdBootEvent((INIT_CMD_INFO *)cookie);
490 return 0;
491 }
492
MODULE_CONSTRUCTOR(void)493 MODULE_CONSTRUCTOR(void)
494 {
495 // Add hook to record time-cost commands
496 HOOK_INFO info = {INIT_CMD_RECORD, 0, RecordInitCmd, NULL};
497 HookMgrAddEx(GetBootStageHookMgr(), &info);
498
499 // Add hook to parse all services with bootevents
500 InitAddServiceParseHook(ServiceParseBootEventHook);
501
502 // Add hook to record start time for services with bootevents
503 InitAddServiceHook(SetServiceBootEventFork, INIT_SERVICE_FORK_AFTER);
504
505 InitAddGlobalInitHook(0, ParamSetBootEventHook);
506 }
507