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 <ctype.h> 16 17#include "init_param.h" 18#include "init_service_manager.h" 19#include "init_utils.h" 20#include "param_manager.h" 21#include "param_message.h" 22#include "param_utils.h" 23#include "trigger_checker.h" 24#include "trigger_manager.h" 25#include "securec.h" 26#include "hookmgr.h" 27#include "bootstage.h" 28 29#define MAX_TRIGGER_COUNT_RUN_ONCE 20 30#define MAX_TRIGGER_NAME_LENGTH 256 31static TriggerWorkSpace g_triggerWorkSpace = {}; 32 33static int DoTriggerExecute_(const TriggerNode *trigger, const char *content, uint32_t size) 34{ 35 PARAM_CHECK(trigger != NULL, return -1, "Invalid trigger"); 36 PARAM_LOGV("Do execute trigger %s type: %d", GetTriggerName(trigger), trigger->type); 37 PARAM_CHECK(trigger->type <= TRIGGER_UNKNOW, return -1, "Invalid trigger type %d", trigger->type); 38 CommandNode *cmd = GetNextCmdNode((JobNode *)trigger, NULL); 39 while (cmd != NULL) { 40#ifndef STARTUP_INIT_TEST 41 DoCmdByIndex(cmd->cmdKeyIndex, cmd->content, &cmd->cfgContext); 42#endif 43 cmd = GetNextCmdNode((JobNode *)trigger, cmd); 44 } 45 return 0; 46} 47 48static int DoTriggerCheckResult(TriggerNode *trigger, const char *content, uint32_t size) 49{ 50 UNUSED(content); 51 UNUSED(size); 52 if (TRIGGER_IN_QUEUE(trigger)) { 53 PARAM_LOGI("DoTiggerExecute trigger %s has been waiting execute", GetTriggerName(trigger)); 54 return 0; 55 } 56 TRIGGER_SET_FLAG(trigger, TRIGGER_FLAGS_QUEUE); 57 PARAM_LOGV("Add trigger %s to execute queue", GetTriggerName(trigger)); 58 ExecuteQueuePush(&g_triggerWorkSpace, trigger); 59 return 0; 60} 61 62static int ExecuteTriggerImmediately(TriggerNode *trigger, const char *content, uint32_t size) 63{ 64 PARAM_CHECK(trigger != NULL, return -1, "Invalid trigger"); 65 PARAM_LOGV("ExecuteTriggerImmediately trigger %s", GetTriggerName(trigger)); 66 TriggerHeader *triggerHead = GetTriggerHeader(&g_triggerWorkSpace, trigger->type); 67 if (triggerHead != NULL) { 68 triggerHead->executeTrigger(trigger, content, size); 69 TRIGGER_CLEAR_FLAG(trigger, TRIGGER_FLAGS_QUEUE); 70 71 if (TRIGGER_TEST_FLAG(trigger, TRIGGER_FLAGS_ONCE)) { 72 FreeTrigger(&g_triggerWorkSpace, trigger); 73 } 74 } 75 return 0; 76} 77 78static void StartTriggerExecute_(TriggerNode *trigger, const char *content, uint32_t size) 79{ 80 TriggerHeader *triggerHead = GetTriggerHeader(&g_triggerWorkSpace, trigger->type); 81 if (triggerHead != NULL) { 82 PARAM_LOGV("StartTriggerExecute_ trigger %s flags:0x%04x", 83 GetTriggerName(trigger), trigger->flags); 84 triggerHead->executeTrigger(trigger, content, size); 85 TRIGGER_CLEAR_FLAG(trigger, TRIGGER_FLAGS_QUEUE); 86 if (TRIGGER_TEST_FLAG(trigger, TRIGGER_FLAGS_SUBTRIGGER)) { // boot && xxx=xxx trigger 87 const char *condition = triggerHead->getCondition(trigger); 88 CheckTrigger(&g_triggerWorkSpace, TRIGGER_UNKNOW, condition, strlen(condition), ExecuteTriggerImmediately); 89 } 90 if (TRIGGER_TEST_FLAG(trigger, TRIGGER_FLAGS_ONCE)) { 91 FreeTrigger(&g_triggerWorkSpace, trigger); 92 } 93 } 94} 95 96static void ExecuteQueueWork(uint32_t maxCount, void (*bootStateChange)(int start, const char *)) 97{ 98 uint32_t executeCount = 0; 99 TriggerNode *trigger = ExecuteQueuePop(&g_triggerWorkSpace); 100 char triggerName[MAX_TRIGGER_NAME_LENGTH] = {0}; 101 while (trigger != NULL) { 102 int ret = strcpy_s(triggerName, sizeof(triggerName), GetTriggerName(trigger)); 103 PARAM_CHECK(ret == 0, return, "strcpy triggerName failed!"); 104 if (bootStateChange != NULL) { 105 bootStateChange(0, triggerName); 106 } 107 108 StartTriggerExecute_(trigger, NULL, 0); 109 if (bootStateChange != NULL) { 110 bootStateChange(1, triggerName); 111 } 112 executeCount++; 113 if (executeCount > maxCount) { 114 break; 115 } 116 trigger = ExecuteQueuePop(&g_triggerWorkSpace); 117 } 118} 119 120PARAM_STATIC void ProcessBeforeEvent(const ParamTaskPtr stream, 121 uint64_t eventId, const uint8_t *content, uint32_t size) 122{ 123 PARAM_LOGV("ProcessBeforeEvent %s ", (char *)content); 124 switch (eventId) { 125 case EVENT_TRIGGER_PARAM: { 126 CheckTrigger(&g_triggerWorkSpace, TRIGGER_PARAM, 127 (const char *)content, size, DoTriggerCheckResult); 128 ExecuteQueueWork(MAX_TRIGGER_COUNT_RUN_ONCE, NULL); 129 break; 130 } 131 case EVENT_TRIGGER_BOOT: { 132 if (g_triggerWorkSpace.bootStateChange != NULL) { 133 g_triggerWorkSpace.bootStateChange(0, (const char *)content); 134 } 135 CheckTrigger(&g_triggerWorkSpace, TRIGGER_BOOT, 136 (const char *)content, size, DoTriggerCheckResult); 137 ExecuteQueueWork(1, NULL); 138 if (g_triggerWorkSpace.bootStateChange != NULL) { 139 g_triggerWorkSpace.bootStateChange(1, (const char *)content); 140 } 141 ExecuteQueueWork(MAX_TRIGGER_COUNT_RUN_ONCE, g_triggerWorkSpace.bootStateChange); 142 break; 143 } 144 case EVENT_TRIGGER_PARAM_WAIT: { 145 CheckTrigger(&g_triggerWorkSpace, TRIGGER_PARAM_WAIT, 146 (const char *)content, size, ExecuteTriggerImmediately); 147 break; 148 } 149 case EVENT_TRIGGER_PARAM_WATCH: { 150 CheckTrigger(&g_triggerWorkSpace, TRIGGER_PARAM_WATCH, 151 (const char *)content, size, ExecuteTriggerImmediately); 152 break; 153 } 154 default: 155 break; 156 } 157} 158 159static void SendTriggerEvent(int type, const char *content, uint32_t contentLen) 160{ 161 PARAM_CHECK(content != NULL, return, "Invalid param"); 162 PARAM_LOGV("SendTriggerEvent type %d content %s", type, content); 163 ParamEventSend(g_triggerWorkSpace.eventHandle, (uint64_t)type, content, contentLen); 164} 165 166void PostParamTrigger(int type, const char *name, const char *value) 167{ 168 PARAM_CHECK(name != NULL && value != NULL, return, "Invalid param"); 169 uint32_t bufferSize = strlen(name) + strlen(value) + 1 + 1 + 1; 170 PARAM_CHECK(bufferSize < (PARAM_CONST_VALUE_LEN_MAX + PARAM_NAME_LEN_MAX + 1 + 1 + 1), 171 return, "bufferSize is longest %d", bufferSize); 172 char *buffer = (char *)calloc(1, bufferSize); 173 PARAM_CHECK(buffer != NULL, return, "Failed to alloc memory for param %s", name); 174 int ret = sprintf_s(buffer, bufferSize - 1, "%s=%s", name, value); 175 PARAM_CHECK(ret > EOK, free(buffer); 176 return, "Failed to copy param"); 177 SendTriggerEvent(type, buffer, strlen(buffer)); 178 free(buffer); 179} 180 181void PostTrigger(EventType type, const char *content, uint32_t contentLen) 182{ 183 PARAM_CHECK(content != NULL && contentLen > 0, return, "Invalid param"); 184 SendTriggerEvent(type, content, contentLen); 185} 186 187static int GetTriggerType(const char *type) 188{ 189 if (strncmp("param:", type, strlen("param:")) == 0) { 190 return TRIGGER_PARAM; 191 } 192 if (strncmp("boot-service:", type, strlen("boot-service:")) == 0) { 193 return TRIGGER_BOOT; 194 } 195 const char *triggerTypeStr[] = { 196 "pre-init", "boot", "early-init", "init", "early-init", "late-init", "post-init", 197 "fs", "early-fs", "post-fs", "late-fs", "early-boot", "post-fs-data", "reboot", "suspend" 198 }; 199 for (size_t i = 0; i < ARRAY_LENGTH(triggerTypeStr); i++) { 200 if (strcmp(triggerTypeStr[i], type) == 0) { 201 return TRIGGER_BOOT; 202 } 203 } 204 return TRIGGER_UNKNOW; 205} 206 207static int GetCommandInfo(const char *cmdLine, int *cmdKeyIndex, char **content) 208{ 209 const char *matchCmd = GetMatchCmd(cmdLine, cmdKeyIndex); 210 PARAM_CHECK(matchCmd != NULL, return -1, "Command not support %s", cmdLine); 211 char *str = strstr(cmdLine, matchCmd); 212 if (str != NULL) { 213 str += strlen(matchCmd); 214 } 215 while (str != NULL && isspace(*str)) { 216 str++; 217 } 218 *content = str; 219 return 0; 220} 221 222static void ParseJobHookExecute(const char *name, const cJSON *jobNode) 223{ 224 JOB_PARSE_CTX context; 225 226 context.jobName = name; 227 context.jobNode = jobNode; 228 229 (void)HookMgrExecute(GetBootStageHookMgr(), INIT_JOB_PARSE, (void *)(&context), NULL); 230} 231 232static int ParseTrigger_(const TriggerWorkSpace *workSpace, 233 const cJSON *triggerItem, int (*checkJobValid)(const char *jobName), const ConfigContext *cfgContext) 234{ 235 PARAM_CHECK(triggerItem != NULL, return -1, "Invalid file"); 236 PARAM_CHECK(workSpace != NULL, return -1, "Failed to create trigger list"); 237 char *name = cJSON_GetStringValue(cJSON_GetObjectItem(triggerItem, "name")); 238 PARAM_CHECK(name != NULL, return -1, "Can not get name from cfg"); 239 char *condition = cJSON_GetStringValue(cJSON_GetObjectItem(triggerItem, "condition")); 240 int type = GetTriggerType(name); 241 PARAM_CHECK(type <= TRIGGER_UNKNOW, return -1, "Failed to get trigger index"); 242 if (type != TRIGGER_BOOT && checkJobValid != NULL && checkJobValid(name) != 0) { 243 PARAM_LOGI("Trigger %s not exist in group", name); 244 return 0; 245 } 246 247 TriggerHeader *header = GetTriggerHeader(workSpace, type); 248 PARAM_CHECK(header != NULL, return -1, "Failed to get header %d", type); 249 JobNode *trigger = UpdateJobTrigger(workSpace, type, condition, name); 250 PARAM_CHECK(trigger != NULL, return -1, "Failed to create trigger %s", name); 251 PARAM_LOGV("ParseTrigger %s type %d count %d", name, type, header->triggerCount); 252 cJSON *cmdItems = cJSON_GetObjectItem(triggerItem, CMDS_ARR_NAME_IN_JSON); 253 if (cmdItems == NULL || !cJSON_IsArray(cmdItems)) { 254 return 0; 255 } 256 int cmdLinesCnt = cJSON_GetArraySize(cmdItems); 257 PARAM_CHECK(cmdLinesCnt > 0, return -1, "Command array size must positive %s", name); 258 259 int ret; 260 int cmdKeyIndex = 0; 261 for (int i = 0; (i < cmdLinesCnt) && (i < TRIGGER_MAX_CMD); ++i) { 262 char *cmdLineStr = cJSON_GetStringValue(cJSON_GetArrayItem(cmdItems, i)); 263 PARAM_CHECK(cmdLineStr != NULL, continue, "Command is null"); 264 265 char *content = NULL; 266 ret = GetCommandInfo(cmdLineStr, &cmdKeyIndex, &content); 267 PARAM_CHECK(ret == 0, continue, "Command not support %s", cmdLineStr); 268 ret = AddCommand(trigger, (uint32_t)cmdKeyIndex, content, cfgContext); 269 PARAM_CHECK(ret == 0, continue, "Failed to add command %s", cmdLineStr); 270 header->cmdNodeCount++; 271 } 272 return 0; 273} 274 275int ParseTriggerConfig(const cJSON *fileRoot, int (*checkJobValid)(const char *jobName), void *context) 276{ 277 PARAM_CHECK(g_triggerWorkSpace.eventHandle != NULL, return -1, "Invalid trigger data"); 278 PARAM_CHECK(fileRoot != NULL, return -1, "Invalid file"); 279 ConfigContext *cfgContext = (ConfigContext *)context; 280 cJSON *triggers = cJSON_GetObjectItemCaseSensitive(fileRoot, TRIGGER_ARR_NAME_IN_JSON); 281 if (triggers == NULL || !cJSON_IsArray(triggers)) { 282 return 0; 283 } 284 int size = cJSON_GetArraySize(triggers); 285 PARAM_CHECK(size > 0, return -1, "Trigger array size must positive"); 286 287 for (int i = 0; i < size && i < TRIGGER_MAX_CMD; ++i) { 288 cJSON *item = cJSON_GetArrayItem(triggers, i); 289 ParseTrigger_(&g_triggerWorkSpace, item, checkJobValid, cfgContext); 290 /* 291 * execute job parsing hooks 292 */ 293 ParseJobHookExecute(cJSON_GetStringValue(cJSON_GetObjectItem(item, "name")), item); 294 } 295 return 0; 296} 297 298int CheckAndMarkTrigger(int type, const char *name) 299{ 300 TriggerHeader *triggerHead = GetTriggerHeader(&g_triggerWorkSpace, type); 301 if (triggerHead) { 302 return triggerHead->checkAndMarkTrigger(&g_triggerWorkSpace, type, name); 303 } 304 return 0; 305} 306 307int InitTriggerWorkSpace(void) 308{ 309 if (g_triggerWorkSpace.eventHandle != NULL) { 310 return 0; 311 } 312 g_triggerWorkSpace.bootStateChange = NULL; 313 ParamEventTaskCreate(&g_triggerWorkSpace.eventHandle, ProcessBeforeEvent); 314 PARAM_CHECK(g_triggerWorkSpace.eventHandle != NULL, return -1, "Failed to event handle"); 315 316 // executeQueue 317 g_triggerWorkSpace.executeQueue.executeQueue = calloc(1, TRIGGER_EXECUTE_QUEUE * sizeof(TriggerNode *)); 318 PARAM_CHECK(g_triggerWorkSpace.executeQueue.executeQueue != NULL, 319 return -1, "Failed to alloc memory for executeQueue"); 320 g_triggerWorkSpace.executeQueue.queueCount = TRIGGER_EXECUTE_QUEUE; 321 g_triggerWorkSpace.executeQueue.startIndex = 0; 322 g_triggerWorkSpace.executeQueue.endIndex = 0; 323 InitTriggerHead(&g_triggerWorkSpace); 324 RegisterTriggerExec(TRIGGER_BOOT, DoTriggerExecute_); 325 RegisterTriggerExec(TRIGGER_PARAM, DoTriggerExecute_); 326 RegisterTriggerExec(TRIGGER_UNKNOW, DoTriggerExecute_); 327 PARAM_LOGV("InitTriggerWorkSpace success"); 328 return 0; 329} 330 331void CloseTriggerWorkSpace(void) 332{ 333 for (size_t i = 0; i < sizeof(g_triggerWorkSpace.triggerHead) / sizeof(g_triggerWorkSpace.triggerHead[0]); i++) { 334 ClearTrigger(&g_triggerWorkSpace, i); 335 } 336 OH_HashMapDestory(g_triggerWorkSpace.hashMap, NULL); 337 g_triggerWorkSpace.hashMap = NULL; 338 free(g_triggerWorkSpace.executeQueue.executeQueue); 339 g_triggerWorkSpace.executeQueue.executeQueue = NULL; 340 ParamTaskClose(g_triggerWorkSpace.eventHandle); 341 g_triggerWorkSpace.eventHandle = NULL; 342} 343 344TriggerWorkSpace *GetTriggerWorkSpace(void) 345{ 346 return &g_triggerWorkSpace; 347} 348 349void RegisterTriggerExec(int type, 350 int32_t (*executeTrigger)(const struct tagTriggerNode_ *, const char *, uint32_t)) 351{ 352 TriggerHeader *triggerHead = GetTriggerHeader(&g_triggerWorkSpace, type); 353 if (triggerHead != NULL) { 354 triggerHead->executeTrigger = executeTrigger; 355 } 356} 357 358void DoTriggerExec(const char *triggerName) 359{ 360 PARAM_CHECK(g_triggerWorkSpace.eventHandle != NULL, return, "Invalid trigger data"); 361 PARAM_CHECK(triggerName != NULL, return, "Invalid param"); 362 JobNode *trigger = GetTriggerByName(&g_triggerWorkSpace, triggerName); 363 if (trigger != NULL && !TRIGGER_IN_QUEUE((TriggerNode *)trigger)) { 364 PARAM_LOGV("Trigger job %s", trigger->name); 365 TRIGGER_SET_FLAG((TriggerNode *)trigger, TRIGGER_FLAGS_QUEUE); 366 ExecuteQueuePush(&g_triggerWorkSpace, (TriggerNode *)trigger); 367 } else { 368 PARAM_LOGW("Can not find trigger %s", triggerName); 369 } 370} 371 372void DoJobExecNow(const char *triggerName) 373{ 374 PARAM_CHECK(g_triggerWorkSpace.eventHandle != NULL, return, "Invalid trigger data"); 375 PARAM_CHECK(triggerName != NULL, return, "Invalid param"); 376 JobNode *trigger = GetTriggerByName(&g_triggerWorkSpace, triggerName); 377 if (trigger != NULL) { 378 if (strncmp(triggerName, "reboot", strlen("reboot")) == 0) { 379 HookMgrExecute(GetBootStageHookMgr(), INIT_SHUT_DETECTOR, NULL, NULL); 380 } 381 StartTriggerExecute_((TriggerNode *)trigger, NULL, 0); 382 } 383} 384 385int AddCompleteJob(const char *name, const char *condition, const char *cmdContent) 386{ 387 PARAM_CHECK(g_triggerWorkSpace.eventHandle != NULL, return -1, "Invalid trigger data"); 388 PARAM_CHECK(name != NULL, return -1, "Invalid name"); 389 PARAM_CHECK(cmdContent != NULL, return -1, "Invalid cmdContent"); 390 int type = GetTriggerType(name); 391 PARAM_CHECK(type <= TRIGGER_UNKNOW, return -1, "Failed to get trigger index"); 392 TriggerHeader *header = GetTriggerHeader(&g_triggerWorkSpace, type); 393 PARAM_CHECK(header != NULL, return -1, "Failed to get header %d", type); 394 395 JobNode *trigger = UpdateJobTrigger(&g_triggerWorkSpace, type, condition, name); 396 PARAM_CHECK(trigger != NULL, return -1, "Failed to create trigger"); 397 char *content = NULL; 398 int cmdKeyIndex = 0; 399 int ret = GetCommandInfo(cmdContent, &cmdKeyIndex, &content); 400 PARAM_CHECK(ret == 0, return -1, "Command not support %s", cmdContent); 401 ret = AddCommand(trigger, (uint32_t)cmdKeyIndex, content, NULL); // use default context 402 PARAM_CHECK(ret == 0, return -1, "Failed to add command %s", cmdContent); 403 header->cmdNodeCount++; 404 PARAM_LOGV("AddCompleteJob %s type %d count %d", name, type, header->triggerCount); 405 return 0; 406} 407 408void RegisterBootStateChange(void (*bootStateChange)(int, const char *)) 409{ 410 if (bootStateChange != NULL) { 411 g_triggerWorkSpace.bootStateChange = bootStateChange; 412 } 413} 414