bootevent.c 14.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdbool.h>
#include "init_module_engine.h"
18
#include "init_cmdexecutor.h"
19 20
#include "trigger_manager.h"
#include "init_log.h"
M
Mupceet 已提交
21
#include "plugin_adapter.h"
M
Mupceet 已提交
22 23 24 25
#include "init_hook.h"
#include "init_service.h"
#include "bootstage.h"
#include "securec.h"
26
#include "init_utils.h"
27
#include "init_cmds.h"
28 29 30

#define BOOT_EVENT_PARA_PREFIX      "bootevent."
#define BOOT_EVENT_PARA_PREFIX_LEN  10
M
Mupceet 已提交
31
#define BOOT_EVENT_TIMESTAMP_MAX_LEN  50
32
#define BOOT_EVENT_FILEPATH_MAX_LEN  60
33
#define BOOT_EVENT_FINISH  2
34 35 36
#define SECTOMSEC  1000000
#define SECTONSEC  1000000000
#define MSECTONSEC  1000
37
#define SAVEINITBOOTEVENTMSEC  100000
38
#define BOOTEVENT_OUTPUT_PATH "/data/service/el0/startup/init/"
C
codex  
chengjinsong 已提交
39
static int g_bootEventNum = 0;
M
Mupceet 已提交
40

41 42 43
// check bootevent enable
static int g_bootEventEnable = 1;

M
Mupceet 已提交
44 45 46 47 48 49
enum {
    BOOTEVENT_FORK,
    BOOTEVENT_READY,
    BOOTEVENT_MAX
};

50 51
typedef struct tagBOOT_EVENT_PARAM_ITEM {
    ListNode    node;
52
    char  *paramName;
53
    int pid;
M
Mupceet 已提交
54
    struct timespec timestamp[BOOTEVENT_MAX];
55 56
} BOOT_EVENT_PARAM_ITEM;

M
Mupceet 已提交
57
static ListNode bootEventList = {&bootEventList, &bootEventList};
58

M
Mupceet 已提交
59
static int BootEventParaListCompareProc(ListNode *node, void *data)
60
{
M
Mupceet 已提交
61 62 63
    BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)node;
    if (strcmp(item->paramName + BOOT_EVENT_PARA_PREFIX_LEN, (const char *)data) == 0) {
        return 0;
64
    }
M
Mupceet 已提交
65
    return -1;
66 67
}

68 69 70 71 72 73 74 75 76
static int ParseBooteventCompareProc(ListNode *node, void *data)
{
    BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)node;
    if (strcmp(item->paramName, (const char *)data) == 0) {
        return 0;
    }
    return -1;
}

M
Mupceet 已提交
77
static int AddServiceBootEvent(const char *serviceName, const char *paramName)
78
{
M
Mupceet 已提交
79 80
    ServiceExtData *extData = NULL;
    ListNode *found = NULL;
81
    if (strncmp(paramName, BOOT_EVENT_PARA_PREFIX, BOOT_EVENT_PARA_PREFIX_LEN) != 0) {
M
Mupceet 已提交
82
        return -1;
83
    }
84
    found = OH_ListFind(&bootEventList, (void *)paramName, ParseBooteventCompareProc);
M
Mupceet 已提交
85 86
    if (found != NULL) {
        return -1;
87
    }
M
Mupceet 已提交
88 89 90 91 92
    for (int i = HOOK_ID_BOOTEVENT; i < HOOK_ID_BOOTEVENT_MAX; i++) {
        extData = AddServiceExtData(serviceName, i, NULL, sizeof(BOOT_EVENT_PARAM_ITEM));
        if (extData != NULL) {
            break;
        }
93
    }
M
Mupceet 已提交
94 95
    if (extData == NULL) {
        return -1;
96
    }
M
Mupceet 已提交
97 98 99 100 101
    BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)extData->data;
    OH_ListInit(&item->node);
    for (int i = 0; i < BOOTEVENT_MAX; i++) {
        item->timestamp[i].tv_nsec = 0;
        item->timestamp[i].tv_sec = 0;
102
    }
M
Mupceet 已提交
103 104
    item->paramName = strdup(paramName);
    if (item->paramName == NULL) {
105
        DelServiceExtData(serviceName, extData->dataId);
M
Mupceet 已提交
106 107
        INIT_LOGI("strdup failed");
        return -1;
108
    }
M
Mupceet 已提交
109 110
    OH_ListAddTail(&bootEventList, (ListNode *)&item->node);
    return 0;
111 112
}

C
cheng_jinsong 已提交
113 114 115 116 117 118 119 120 121 122
static void AddInitBootEvent(const char *bootEventName)
{
    ListNode *found = NULL;
    found = OH_ListFind(&bootEventList, (void *)bootEventName, ParseBooteventCompareProc);
    if (found != NULL) {
        INIT_CHECK_ONLY_RETURN(clock_gettime(CLOCK_MONOTONIC,
            &(((BOOT_EVENT_PARAM_ITEM *)found)->timestamp[BOOTEVENT_READY])) == 0);
        return;
    }

123
    BOOT_EVENT_PARAM_ITEM *item = calloc(1, sizeof(BOOT_EVENT_PARAM_ITEM));
C
cheng_jinsong 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
    if (item == NULL) {
        return;
    }
    OH_ListInit(&item->node);
    if (clock_gettime(CLOCK_MONOTONIC, &(item->timestamp[BOOTEVENT_FORK])) != 0) {
        free(item);
        return;
    }
    item->paramName = strdup(bootEventName);
    if (item->paramName == NULL) {
        free(item);
        return;
    }
    OH_ListAddTail(&bootEventList, (ListNode *)&item->node);
    return;
}

141 142
#define BOOT_EVENT_BOOT_COMPLETED "bootevent.boot.completed"

143 144 145 146 147 148 149 150 151
static void BootEventDestroy(ListNode *node)
{
    BOOT_EVENT_PARAM_ITEM *bootEvent = (BOOT_EVENT_PARAM_ITEM *)node;
    INIT_CHECK(bootEvent->paramName == NULL, free((void *)bootEvent->paramName));
    free((void *)bootEvent);
}

static int SaveServiceBootEvent();

152 153
static void BootEventParaFireByName(const char *paramName)
{
M
Mupceet 已提交
154 155 156
    ListNode *found = NULL;
    char *bootEventValue = strrchr(paramName, '.');
    if (bootEventValue == NULL) {
157 158
        return;
    }
M
Mupceet 已提交
159
    bootEventValue[0] = '\0';
160

M
Mupceet 已提交
161 162 163
    found = OH_ListFind(&bootEventList, (void *)paramName, BootEventParaListCompareProc);
    if (found == NULL) {
        return;
164
    }
M
Mupceet 已提交
165 166 167 168 169
    if (((BOOT_EVENT_PARAM_ITEM *)found)->timestamp[BOOTEVENT_READY].tv_sec != 0) {
        return;
    }
    INIT_CHECK_ONLY_RETURN(clock_gettime(CLOCK_MONOTONIC,
        &(((BOOT_EVENT_PARAM_ITEM *)found)->timestamp[BOOTEVENT_READY])) == 0);
C
codex  
chengjinsong 已提交
170
    g_bootEventNum--;
171
    // Check if all boot event params are fired
C
codex  
chengjinsong 已提交
172
    if (g_bootEventNum > 0) {
173 174 175
        return;
    }
    // All parameters are fired, set boot completed now ...
C
cheng_jinsong 已提交
176
    INIT_LOGI("All boot events are fired, boot complete now ...");
177
    SystemWriteParam(BOOT_EVENT_BOOT_COMPLETED, "true");
178 179 180 181 182
    g_bootEventEnable = BOOT_EVENT_FINISH;
    SaveServiceBootEvent();
    const char *clearBootEventArgv[] = {"bootevent"};
    // clear servie extra data
    PluginExecCmd("clear", ARRAY_LENGTH(clearBootEventArgv), clearBootEventArgv);
C
cheng_jinsong 已提交
183 184

    HookMgrExecute(GetBootStageHookMgr(), INIT_BOOT_COMPLETE, NULL, NULL);
M
Mupceet 已提交
185
    return;
186 187 188 189 190
}

#define BOOT_EVENT_FIELD_NAME "bootevents"
static void ServiceParseBootEventHook(SERVICE_PARSE_CTX *serviceParseCtx)
{
191 192 193
    if (g_bootEventEnable == 0) {
        return;
    }
M
Mupceet 已提交
194 195 196
    int cnt;
    cJSON *bootEvents = cJSON_GetObjectItem(serviceParseCtx->serviceNode, BOOT_EVENT_FIELD_NAME);

C
cheng_jinsong 已提交
197
    // No boot events in config file
M
Mupceet 已提交
198 199 200
    if (bootEvents == NULL) {
        return;
    }
201 202 203
    SERVICE_INFO_CTX ctx = {0};
    ctx.serviceName = serviceParseCtx->serviceName;
    HookMgrExecute(GetBootStageHookMgr(), INIT_SERVICE_CLEAR, (void *)&ctx, NULL);
C
cheng_jinsong 已提交
204
    // Single boot event in config file
M
Mupceet 已提交
205 206 207
    if (!cJSON_IsArray(bootEvents)) {
        if (AddServiceBootEvent(serviceParseCtx->serviceName,
            cJSON_GetStringValue(bootEvents)) != 0) {
C
cheng_jinsong 已提交
208
            INIT_LOGI("Add service bootEvent failed %s", serviceParseCtx->serviceName);
M
Mupceet 已提交
209 210
            return;
        }
C
codex  
chengjinsong 已提交
211
        g_bootEventNum++;
M
Mupceet 已提交
212 213 214
        return;
    }

C
cheng_jinsong 已提交
215
    // Multiple boot events in config file
M
Mupceet 已提交
216 217 218 219 220
    cnt = cJSON_GetArraySize(bootEvents);
    for (int i = 0; i < cnt; i++) {
        cJSON *item = cJSON_GetArrayItem(bootEvents, i);
        if (AddServiceBootEvent(serviceParseCtx->serviceName,
            cJSON_GetStringValue(item)) != 0) {
C
cheng_jinsong 已提交
221
            INIT_LOGI("Add service bootEvent failed %s", serviceParseCtx->serviceName);
222
            continue;
M
Mupceet 已提交
223
        }
C
codex  
chengjinsong 已提交
224
        g_bootEventNum++;
M
Mupceet 已提交
225
    }
M
Mupceet 已提交
226
}
227

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
static void AddCmdBootEvent(int argc, const char **argv)
{
    if (argc < 4) { // 4 is min args cmd boot event required
        return;
    }
    
    BOOT_EVENT_PARAM_ITEM *item = calloc(1, sizeof(BOOT_EVENT_PARAM_ITEM));
    if (item == NULL) {
        return;
    }
    OH_ListInit(&item->node);
    item->timestamp[BOOTEVENT_FORK] = ((INIT_TIMING_STAT *)argv[3])->startTime; // 3 args
    item->timestamp[BOOTEVENT_READY] = ((INIT_TIMING_STAT *)argv[3])->endTime; // 3 args
    int cmdLen = strlen(argv[1]) + strlen(argv[2]) + 1; // 2 args 1 '\0'
    item->paramName = calloc(1, cmdLen);
    if (item->paramName == NULL) {
        free(item);
        return;
    }
    for (int i = 1; i < 3; i++) { // 3 cmd content end
        INIT_CHECK_ONLY_ELOG(strcat_s(item->paramName, cmdLen, argv[i]) >= 0, "combine cmd args failed");
    }
    OH_ListAddTail(&bootEventList, (ListNode *)&item->node);
    return;
}

M
Mupceet 已提交
254 255
static int DoBootEventCmd(int id, const char *name, int argc, const char **argv)
{
256 257 258 259 260 261 262 263 264 265 266 267
    if (g_bootEventEnable == BOOT_EVENT_FINISH) {
        return 0;
    }
    // clear init boot events that recorded before persist param read
    if (g_bootEventEnable == 0) {
        const char *clearBootEventArgv[] = {"bootevent"};
        PluginExecCmd("clear", ARRAY_LENGTH(clearBootEventArgv), clearBootEventArgv);
        OH_ListRemoveAll(&bootEventList, BootEventDestroy);
        g_bootEventEnable = BOOT_EVENT_FINISH;
        return 0;
    }

M
Mupceet 已提交
268
    PLUGIN_CHECK(argc >= 1, return -1, "Invalid parameter");
C
cheng_jinsong 已提交
269
    if (strcmp(argv[0], "init") == 0) {
270 271 272
        if (argc < 2) { // 2 args
            return 0;
        }
C
cheng_jinsong 已提交
273
        AddInitBootEvent(argv[1]);
274 275
    } else if (strcmp(argv[0], "cmd") == 0) {
        AddCmdBootEvent(argc, argv);
C
cheng_jinsong 已提交
276 277 278 279
    } else {
        // argv[0] samgr.ready.true
        BootEventParaFireByName(argv[0]);
    }
M
Mupceet 已提交
280 281
    return 0;
}
282

283
static int AddItemToJson(cJSON *root, const char *name, double startTime, int pid, double durTime)
284 285 286 287
{
    cJSON *obj = cJSON_CreateObject(); // release obj at traverse done
    INIT_CHECK_RETURN_VALUE(obj != NULL, -1);
    cJSON_AddStringToObject(obj, "name", name);
C
cheng_jinsong 已提交
288
    cJSON_AddNumberToObject(obj, "ts", startTime);
289
    cJSON_AddStringToObject(obj, "ph", "X");
290 291
    cJSON_AddNumberToObject(obj, "pid", pid);
    cJSON_AddNumberToObject(obj, "tid", pid);
292 293 294 295 296
    cJSON_AddNumberToObject(obj, "dur", durTime);
    cJSON_AddItemToArray(root, obj);
    return 0;
}

C
cheng_jinsong 已提交
297
static int BootEventTraversal(ListNode *node, void *root)
298
{
299
    static int start = 0;
300 301 302 303 304 305
    BOOT_EVENT_PARAM_ITEM *item = (BOOT_EVENT_PARAM_ITEM *)node;
    double forkTime = item->timestamp[BOOTEVENT_FORK].tv_sec * SECTOMSEC +
        (double)item->timestamp[BOOTEVENT_FORK].tv_nsec / MSECTONSEC;
    double readyTime = item->timestamp[BOOTEVENT_READY].tv_sec * SECTOMSEC +
        (double)item->timestamp[BOOTEVENT_READY].tv_nsec / MSECTONSEC;
    double durTime = readyTime - forkTime;
306 307 308 309 310 311 312 313
    if (item->pid == 0) {
        if (durTime < SAVEINITBOOTEVENTMSEC) {
            return 0;
        }
        item->pid = 1; // 1 is init pid
    }
    if (start == 0) {
        // set trace start time 0
314 315
        INIT_CHECK_RETURN_VALUE(AddItemToJson((cJSON *)root, item->paramName, 0,
            1, 0) == 0, -1);
316
        start++;
317 318
    }
    INIT_CHECK_RETURN_VALUE(AddItemToJson((cJSON *)root, item->paramName, forkTime,
319
        item->pid, durTime > 0 ? durTime : 0) == 0, -1);
320 321 322
    return 0;
}

323
static int SaveServiceBootEvent()
324
{
325 326 327
    if (g_bootEventEnable == 0) {
        return 0;
    }
328 329 330 331
    time_t nowTime = time(NULL);
    INIT_CHECK_RETURN_VALUE(nowTime > 0, -1);
    struct tm *p = localtime(&nowTime);
    INIT_CHECK_RETURN_VALUE(p != NULL, -1);
C
cheng_jinsong 已提交
332 333
    char bootEventFileName[BOOT_EVENT_FILEPATH_MAX_LEN] = "";
    INIT_CHECK_RETURN_VALUE(snprintf(bootEventFileName, BOOT_EVENT_FILEPATH_MAX_LEN,
334 335
        BOOTEVENT_OUTPUT_PATH"%d%d%d-%d%d.bootevent",
        1900 + p->tm_year, p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min) >= 0, -1); // 1900 is start year
C
cheng_jinsong 已提交
336 337
    CheckAndCreatFile(bootEventFileName, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    FILE *tmpFile = fopen(bootEventFileName, "wr");
338 339
    INIT_CHECK_RETURN_VALUE(tmpFile != NULL, -1);
    cJSON *root = cJSON_CreateArray();
C
cheng_jinsong 已提交
340 341 342 343 344
    if (root == NULL) {
        (void)fclose(tmpFile);
        return -1;
    }
    OH_ListTraversal(&bootEventList, (void *)root, BootEventTraversal, 0);
345
    char *buff = cJSON_Print(root);
C
cheng_jinsong 已提交
346 347 348 349 350 351
    if (buff == NULL) {
        cJSON_Delete(root);
        (void)fclose(tmpFile);
        return -1;
    }
    INIT_CHECK_ONLY_ELOG(fprintf(tmpFile, "%s\n", buff) >= 0, "save boot event file failed");
352 353 354 355 356 357 358
    free(buff);
    cJSON_Delete(root);
    (void)fflush(tmpFile);
    (void)fclose(tmpFile);
    return 0;
}

M
Mupceet 已提交
359 360
static int32_t g_executorId = -1;
static int ParamSetBootEventHook(const HOOK_INFO *hookInfo, void *cookie)
361
{
M
Mupceet 已提交
362 363
    if (g_executorId == -1) {
        g_executorId = AddCmdExecutor("bootevent", DoBootEventCmd);
364
    }
M
Mupceet 已提交
365
    return 0;
366 367
}

M
Mupceet 已提交
368 369 370 371 372 373 374 375
static void ClearServiceBootEvent(SERVICE_INFO_CTX *serviceCtx)
{
    if (serviceCtx->reserved == NULL || strcmp(serviceCtx->reserved, "bootevent") == 0) {
        for (int i = HOOK_ID_BOOTEVENT; i < HOOK_ID_BOOTEVENT_MAX; i++) {
            ServiceExtData *extData = GetServiceExtData(serviceCtx->serviceName, i);
            if (extData == NULL) {
                return;
            }
376
            free(((BOOT_EVENT_PARAM_ITEM *)extData->data)->paramName);
M
Mupceet 已提交
377 378
            OH_ListRemove(&((BOOT_EVENT_PARAM_ITEM *)extData->data)->node);
            DelServiceExtData(serviceCtx->serviceName, i);
379
            g_bootEventNum--;
M
Mupceet 已提交
380
        }
381 382 383 384 385 386 387
        // clear service extra data first
        return;
    }
    if (strcmp(serviceCtx->reserved, "clearInitBootevent") == 0) {
        // clear init boot event
        OH_ListRemoveAll(&bootEventList, BootEventDestroy);
        g_bootEventNum = 0;
M
Mupceet 已提交
388 389 390 391 392 393
    }
    return;
}

static void SetServiceBootEventFork(SERVICE_INFO_CTX *serviceCtx)
{
394 395 396
    if (g_bootEventEnable == 0) {
        return;
    }
M
Mupceet 已提交
397 398
    for (int i = HOOK_ID_BOOTEVENT; i < HOOK_ID_BOOTEVENT_MAX; i++) {
        ServiceExtData *extData = GetServiceExtData(serviceCtx->serviceName, i);
399
        if (extData == NULL) {
M
Mupceet 已提交
400 401
            return;
        }
402 403 404 405
        if (serviceCtx->reserved != NULL) {
            ((BOOT_EVENT_PARAM_ITEM *)extData->data)->pid = *((int *)serviceCtx->reserved);
            continue;
        }
M
Mupceet 已提交
406 407 408 409 410 411
        INIT_CHECK_ONLY_RETURN(clock_gettime(CLOCK_MONOTONIC,
            &(((BOOT_EVENT_PARAM_ITEM *)extData->data)->timestamp[BOOTEVENT_FORK])) == 0);
    }
    return;
}

412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
static int GetBootEventFlag(const HOOK_INFO *info, void *cookie)
{
    char BootEventOpen[6] = ""; // 6 is length of bool value
    uint32_t len = sizeof(BootEventOpen);
    SystemReadParam("persist.init.bootevent.enable", BootEventOpen, &len);
    if (strcmp(BootEventOpen, "true") != 0) {
        g_bootEventEnable = 0;
    }
    return 0;
}

static int RecordInitCmd(const HOOK_INFO *info, void *cookie)
{
    INIT_CMD_INFO *cmdCtx = (INIT_CMD_INFO *)cookie;
    const char *bootEventArgv[] = {"cmd", cmdCtx->cmdName, cmdCtx->cmdContent, cmdCtx->reserved};
    return DoBootEventCmd(0, NULL, ARRAY_LENGTH(bootEventArgv), bootEventArgv);
}

430 431
MODULE_CONSTRUCTOR(void)
{
432 433
    HOOK_INFO info = {INIT_CMD_RECORD, 0, RecordInitCmd, NULL};
    HookMgrAddEx(GetBootStageHookMgr(), &info);
M
Mupceet 已提交
434
    InitAddServiceHook(SetServiceBootEventFork, INIT_SERVICE_FORK_BEFORE);
435
    InitAddServiceHook(SetServiceBootEventFork, INIT_SERVICE_FORK_AFTER);
M
Mupceet 已提交
436
    InitAddServiceHook(ClearServiceBootEvent, INIT_SERVICE_CLEAR);
437
    InitAddServiceParseHook(ServiceParseBootEventHook);
M
Mupceet 已提交
438
    InitAddGlobalInitHook(0, ParamSetBootEventHook);
439
    InitAddPostPersistParamLoadHook(0, GetBootEventFlag);
440
}