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