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