feat: Add bootevent polling mechanism

1) 为parameter service增加ParamSetHookAdd接口扩展set时的hook
2)为bootstage增加InitAddServiceParseHook接口扩展解析service的hook
3)增加bootevent静态模块,在hook中解析bootevents字段
4)在bootevent静态模块中扩展hook处理parameter set时的bootevent.参数
5)所有的bootevent投票完成时,设置bootevent.boot.completed
6)去掉service解析时的字段校验。

Signed-off-by: handyohos <zhangxiaotian@huawei.com>
Change-Id: I7c55cd6319ff7dd773c1e3b43b2c889cceaa3a0d
This commit is contained in:
handyohos 2022-06-27 23:03:05 +08:00
parent 1261fd89ca
commit fd818084b9
11 changed files with 439 additions and 34 deletions

View File

@ -28,6 +28,7 @@ if (defined(ohos_lite)) {
"//base/startup/init_lite/services/include",
"//base/startup/init_lite/interfaces/innerkits/include",
"//base/startup/init_lite/services/log",
"//third_party/cJSON",
]
}
@ -65,6 +66,7 @@ if (defined(ohos_lite)) {
"//base/startup/init_lite/interfaces/innerkits/include",
"//base/startup/init_lite/services/include",
"//base/startup/init_lite/services/log",
"//third_party/cJSON",
]
}

View File

@ -17,6 +17,7 @@
#define BASE_STARTUP_BOOTSTAGE_H
#include "hookmgr.h"
#include "cJSON.h"
#ifdef __cplusplus
#if __cplusplus
@ -29,6 +30,7 @@ enum INIT_BOOTSTAGE {
INIT_PRE_PARAM_SERVICE = 10,
INIT_PRE_PARAM_LOAD = 20,
INIT_PRE_CFG_LOAD = 30,
INIT_SERVICE_PARSE = 35,
INIT_POST_PERSIST_PARAM_LOAD = 40,
INIT_POST_CFG_LOAD = 50
};
@ -64,6 +66,32 @@ inline int InitAddPostPersistParamLoadHook(int prio, OhosHook hook)
{
return HookMgrAdd(GetBootStageHookMgr(), INIT_POST_PERSIST_PARAM_LOAD, prio, hook);
}
/**
* @brief service config parsing context information
*/
typedef struct tagSERVICE_PARSE_CTX {
const char *serviceName; /* Service name */
const cJSON *serviceNode; /* Service JSON node */
} SERVICE_PARSE_CTX;
/**
* @brief service config parse hook function prototype
*
* @param serviceParseCtx service config parsing context information
* @return None
*/
typedef void (*ServiceParseHook)(SERVICE_PARSE_CTX *serviceParseCtx);
/**
* @brief Register a hook for service config parsing
*
* @param hook service config parsing hook
* in the hook, we can parse extra fields in the config file.
* @return return 0 if succeed; other values if failed.
*/
int InitAddServiceParseHook(ServiceParseHook hook);
#ifdef __cplusplus
#if __cplusplus
}

View File

@ -22,6 +22,7 @@
#include "bootstage.h"
#include "init_modulemgr.h"
#include "init_cmdexecutor.h"
#include "init_running_hooks.h"
#ifdef __cplusplus
#if __cplusplus

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef BASE_STARTUP_BOOT_RUNNING_HOOKS_H
#define BASE_STARTUP_BOOT_RUNNING_HOOKS_H
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif
/**
* @brief Running hook stage definition for init
*/
/* Hook stage for setting parameters */
#define INIT_PARAM_SET_HOOK_STAGE 0
/**
* @brief parameter setting context information
*/
typedef struct tagPARAM_SET_CTX {
const char *name; /* Parameter name */
const char *value; /* Parameter value */
/* Skip setting parameter if true
* When setting parameters, parameter service will save the the value by default
* If set skipParamSet in the hook, parameter service will not save
*/
int skipParamSet;
} PARAM_SET_CTX;
/**
* @brief set parameter hook function prototype
*
* @param paramSetCtx parameter setting context information
* @return None
*/
typedef void (*ParamSetHook)(PARAM_SET_CTX *paramSetCtx);
/**
* @brief Register a hook for setting parameters
*
* @param hook parameter setting hook
* in the hook, we can match the parameters with special patterns to do extra controls.
* For example, ohos.ctl.start will control services besides the normal parameter saving.
* @return return 0 if succeed; other values if failed.
*/
int ParamSetHookAdd(ParamSetHook hook);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
#endif

27
services/include/list.h Normal file → Executable file
View File

@ -35,7 +35,34 @@ extern "C" {
* |------| |------| |------|
* | extra| | extra| | extra|
* |______| |______| |______|
*
* Usage Example:
* 1. Define structure for each item in the list
* typedef struct tagTEST_LIST_ITEM {
* ListNode node;
* int value;
* } TEST_LIST_ITEM;
*
* 2. Define a list and init list by ListAddTail
* ListNode testList;
* c(&testList);
*
* 3. Define a list item instance
* TEST_LIST_ITEM item;
* item.value = 0;
*
* 4. Add list item to list
* ListAddTail(&testList, (ListNode *)(&item));
*
* 5. Advanced usage: add with order
* // Ordering compare function
* static int TestListItemCompareProc(ListNode *node, ListNode *newNode)
* {
* TEST_LIST_ITEM *left = (TEST_LIST_ITEM *)node;
* TEST_LIST_ITEM *right = (TEST_LIST_ITEM *)newNode;
* return left->value - right->value;
* }
* ListAddWithOrder(&testList, (ListNode *)(&item))
*/
/**

82
services/init/init_service_manager.c Normal file → Executable file
View File

@ -39,6 +39,11 @@
# include "init_selinux_param.h"
#endif // WITH_SELINUX
#ifndef OHOS_LITE
#include "hookmgr.h"
#include "bootstage.h"
#endif
// All service processes that init will fork+exec.
static ServiceSpace g_serviceSpace = { 0 };
static const int CRITICAL_DEFAULT_CRASH_TIME = 20;
@ -639,35 +644,6 @@ static int GetServiceOnDemand(const cJSON *curArrItem, Service *curServ)
return SERVICE_SUCCESS;
}
static int CheckServiceKeyName(const cJSON *curService)
{
char *cfgServiceKeyList[] = {
"name", "path", "uid", "gid", "once", "importance", "caps", "disabled",
"writepid", "critical", "socket", "console", "file", "ondemand",
"d-caps", "apl", "jobs", "start-mode", "end-mode", "cpucore", "secon", "sandbox",
"permission", "permission_acls"
};
INIT_CHECK_RETURN_VALUE(curService != NULL, SERVICE_FAILURE);
cJSON *child = curService->child;
INIT_CHECK_RETURN_VALUE(child != NULL, SERVICE_FAILURE);
while (child != NULL) {
int i = 0;
int keyListSize = ARRAY_LENGTH(cfgServiceKeyList);
for (; i < keyListSize; i++) {
if (strcmp(child->string, cfgServiceKeyList[i]) == 0) {
break;
}
}
if (i < keyListSize) {
child = child->next;
} else {
INIT_LOGE("CheckServiceKeyName, key name %s is not found. error.", child->string);
return SERVICE_FAILURE;
}
}
return SERVICE_SUCCESS;
}
static int GetServiceMode(Service *service, const cJSON *json)
{
const InitArgInfo startModeMap[] = {
@ -940,6 +916,42 @@ int ParseOneService(const cJSON *curItem, Service *service)
return ret;
}
#ifndef OHOS_LITE
/**
* Service Config File Parse Hooking
*/
static int ServiceParseHookWrapper(const HOOK_INFO *hookInfo, void *executionContext)
{
SERVICE_PARSE_CTX *serviceParseContext = (SERVICE_PARSE_CTX *)executionContext;
ServiceParseHook realHook = (ServiceParseHook)hookInfo->hookCookie;
realHook(serviceParseContext);
return 0;
};
int InitAddServiceParseHook(ServiceParseHook hook)
{
HOOK_INFO info;
info.stage = INIT_SERVICE_PARSE;
info.prio = 0;
info.hook = ServiceParseHookWrapper;
info.hookCookie = (void *)hook;
return HookMgrAddEx(GetBootStageHookMgr(), &info);
}
static void ParseServiceHookExecute(const char *name, const cJSON *serviceNode)
{
SERVICE_PARSE_CTX context;
context.serviceName = name;
context.serviceNode = serviceNode;
(void)HookMgrExecute(GetBootStageHookMgr(), INIT_SERVICE_PARSE, (void *)(&context), NULL);
}
#endif
void ParseAllServices(const cJSON *fileRoot)
{
int servArrSize = 0;
@ -958,10 +970,6 @@ void ParseAllServices(const cJSON *fileRoot)
INIT_LOGE("Failed to get service name %s", serviceName);
continue;
}
if (CheckServiceKeyName(curItem) != SERVICE_SUCCESS) { // invalid service
INIT_LOGE("Invalid service name %s", serviceName);
continue;
}
Service *service = GetServiceByName(serviceName);
if (service != NULL) {
INIT_LOGE("Service name %s has been exist", serviceName);
@ -987,6 +995,14 @@ void ParseAllServices(const cJSON *fileRoot)
FreeServiceFile(service->fileCfg);
service->fileCfg = NULL;
}
#ifndef OHOS_LITE
/*
* Execute service parsing hooks
*/
ParseServiceHookExecute(serviceName, curItem);
#endif
ret = GetCmdLinesFromJson(cJSON_GetObjectItem(curItem, "onrestart"), &service->restartArg);
INIT_CHECK(ret == SERVICE_SUCCESS, service->restartArg = NULL);
}

View File

@ -53,7 +53,10 @@ if (!defined(ohos_lite)) {
group("static_modules") {
if (!defined(ohos_lite)) {
deps = [ ":libbootchart_static" ]
deps = [
":libbootchart_static",
"bootevent:libbootevent_static",
]
}
}

View File

@ -0,0 +1,29 @@
# Copyright (c) 2020-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.
import("//build/ohos.gni")
config("bootevent_static_config") {
include_dirs = [
"//base/startup/init_lite/services/param/linux",
"//base/startup/init_lite/services/loopevent/include",
"//base/startup/init_lite/services/param/include",
"//base/startup/init_lite/services/include/param",
]
}
ohos_source_set("libbootevent_static") {
sources = [ "bootevent.c" ]
public_configs = [ ":bootevent_static_config" ]
public_configs += [ "//base/startup/init_lite/interfaces/innerkits/init_module_engine:init_module_engine_exported_config" ]
}

View File

@ -0,0 +1,175 @@
/*
* 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"
#include "trigger_manager.h"
#include "init_log.h"
#define BOOT_EVENT_PARA_PREFIX "bootevent."
#define BOOT_EVENT_PARA_PREFIX_LEN 10
typedef struct tagBOOT_EVENT_PARAM_ITEM {
ListNode node;
const char *paramName;
} BOOT_EVENT_PARAM_ITEM;
static ListNode *bootEventList = NULL;
static ListNode *getBootEventParaList(bool autoCreate)
{
if (!autoCreate) {
return bootEventList;
}
if (bootEventList != NULL) {
return bootEventList;
}
// Create list node
bootEventList = (ListNode *)malloc(sizeof(ListNode));
if (bootEventList == NULL) {
return NULL;
}
ListInit(bootEventList);
return bootEventList;
}
static void BootEventParaAdd(const char *paramName)
{
ListNode *list;
BOOT_EVENT_PARAM_ITEM *item;
if (paramName == NULL) {
return;
}
// Only bootevent. parameters can be added
if (strncmp(paramName, BOOT_EVENT_PARA_PREFIX, BOOT_EVENT_PARA_PREFIX_LEN) != 0) {
return;
}
INIT_LOGI("Add bootevent [%s] ...", paramName);
list = getBootEventParaList(true);
if (list == NULL) {
return;
}
// Create item
item = (BOOT_EVENT_PARAM_ITEM *)malloc(sizeof(BOOT_EVENT_PARAM_ITEM));
if (item == NULL) {
return;
}
item->paramName = strdup(paramName);
if (item->paramName == NULL) {
free((void *)item);
return;
}
// Add to list
ListAddTail(list, (ListNode *)item);
}
static int BootEventParaListCompareProc(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;
}
static void BootEventParaItemDestroy(BOOT_EVENT_PARAM_ITEM *item)
{
if (item->paramName != NULL) {
free((void *)item->paramName);
}
free((void *)item);
}
#define BOOT_EVENT_BOOT_COMPLETED "bootevent.boot.completed"
static void BootEventParaFireByName(const char *paramName)
{
ListNode *found;
if (bootEventList == NULL) {
return;
}
found = ListFind(getBootEventParaList(false), (void *)paramName, BootEventParaListCompareProc);
if (found != NULL) {
// Remove from list
ListRemove(found);
BootEventParaItemDestroy((BOOT_EVENT_PARAM_ITEM *)found);
}
// Check if all boot event params are fired
if (ListGetCnt(getBootEventParaList(false)) > 0) {
return;
}
// Delete hooks for boot event
free((void *)bootEventList);
bootEventList = NULL;
// All parameters are fired, set boot completed now ...
INIT_LOGI("All bootevents are fired, boot complete now ...");
SystemWriteParam(BOOT_EVENT_BOOT_COMPLETED, "true");
}
#define BOOT_EVENT_FIELD_NAME "bootevents"
static void ServiceParseBootEventHook(SERVICE_PARSE_CTX *serviceParseCtx)
{
int cnt;
cJSON *bootEvents = cJSON_GetObjectItem(serviceParseCtx->serviceNode, BOOT_EVENT_FIELD_NAME);
// No bootevents in config file
if (bootEvents == NULL) {
return;
}
// Single bootevent in config file
if (!cJSON_IsArray(bootEvents)) {
BootEventParaAdd(cJSON_GetStringValue(bootEvents));
return;
}
// Multiple bootevents in config file
cnt = cJSON_GetArraySize(bootEvents);
for (int i = 0; i < cnt; i++) {
cJSON *item = cJSON_GetArrayItem(bootEvents, i);
BootEventParaAdd(cJSON_GetStringValue(item));
}
}
static void ParamSetBootEventHook(PARAM_SET_CTX *paramSetCtx)
{
// Check if the parameter is started with "bootevent."
if (strncmp(paramSetCtx->name, BOOT_EVENT_PARA_PREFIX, BOOT_EVENT_PARA_PREFIX_LEN) != 0) {
return;
}
// Delete the bootevent param for list
BootEventParaFireByName(paramSetCtx->name);
}
MODULE_CONSTRUCTOR(void)
{
InitAddServiceParseHook(ServiceParseBootEventHook);
ParamSetHookAdd(ParamSetBootEventHook);
}

2
services/param/linux/BUILD.gn Normal file → Executable file
View File

@ -31,6 +31,7 @@ param_include_dirs = [
"//base/startup/init_lite/services/include",
"//base/startup/init_lite/services/init/include",
"//base/startup/init_lite/services/log",
"//base/startup/init_lite/interfaces/innerkits/init_module_engine/include",
"//base/startup/init_lite/services/loopevent/include",
"//third_party/bounds_checking_function/include",
"//third_party/cJSON",
@ -75,6 +76,7 @@ if (defined(ohos_lite)) {
defines += [
"_GNU_SOURCE",
"__LINUX__",
"OHOS_LITE",
"INCREMENTAL_VERSION=\"${ohos_version}\"",
"BUILD_TYPE=\"${ohos_build_type}\"",
"BUILD_USER=\"${ohos_build_user}\"",

54
services/param/linux/param_service.c Normal file → Executable file
View File

@ -31,6 +31,10 @@
#include "param_message.h"
#include "trigger_manager.h"
#include "securec.h"
#ifndef OHOS_LITE
#include "hookmgr.h"
#include "init_running_hooks.h"
#endif
static ParamService g_paramService = {};
@ -137,6 +141,45 @@ static int SendWatcherNotifyMessage(const TriggerExtInfo *extData, const char *c
return 0;
}
#ifndef OHOS_LITE
/**
* Parameter Set Hooking
*/
static int ParamSetHookWrapper(const HOOK_INFO *hookInfo, void *executionContext)
{
PARAM_SET_CTX *paramSetContext = (PARAM_SET_CTX *)executionContext;
ParamSetHook realHook = (ParamSetHook)hookInfo->hookCookie;
realHook(paramSetContext);
return 0;
};
int ParamSetHookAdd(ParamSetHook hook)
{
HOOK_INFO info;
info.stage = INIT_PARAM_SET_HOOK_STAGE;
info.prio = 0;
info.hook = ParamSetHookWrapper;
info.hookCookie = (void *)hook;
return HookMgrAddEx(NULL, &info);
}
static int ParamSetHookExecute(const char *name, const char *value)
{
PARAM_SET_CTX context;
context.name = name;
context.value = value;
context.skipParamSet = 0;
(void)HookMgrExecute(NULL, INIT_PARAM_SET_HOOK_STAGE, (void *)(&context), NULL);
return context.skipParamSet;
}
#endif
static int SystemSetParam(const char *name, const char *value, const ParamSecurityLabel *srcLabel)
{
PARAM_LOGV("SystemWriteParam name %s value: %s", name, value);
@ -144,6 +187,17 @@ static int SystemSetParam(const char *name, const char *value, const ParamSecuri
int ret = CheckParameterSet(name, value, srcLabel, &ctrlService);
PARAM_CHECK(ret == 0, return ret, "Forbid to set parameter %s", name);
#ifndef OHOS_LITE
/*
* Execute param set hooks before setting parameter values
*/
if (ParamSetHookExecute(name, value)) {
// Hook has processed the parameter set request
PARAM_LOGI("param [%s] set in the hook.", name);
return 0;
}
#endif
if (ctrlService & PARAM_CTRL_SERVICE) { // ctrl param
PostParamTrigger(EVENT_TRIGGER_PARAM, name, value);
} else {