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
31 static TriggerWorkSpace g_triggerWorkSpace = {};
32 
DoTriggerExecute_(const TriggerNode * trigger,const char * content,uint32_t size)33 static 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 
DoTriggerCheckResult(TriggerNode * trigger,const char * content,uint32_t size)48 static 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 
ExecuteTriggerImmediately(TriggerNode * trigger,const char * content,uint32_t size)62 static 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 
StartTriggerExecute_(TriggerNode * trigger,const char * content,uint32_t size)78 static 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 
ExecuteQueueWork(uint32_t maxCount,void (* bootStateChange)(int start,const char *))96 static 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 
ProcessBeforeEvent(const ParamTaskPtr stream,uint64_t eventId,const uint8_t * content,uint32_t size)120 PARAM_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 
SendTriggerEvent(int type,const char * content,uint32_t contentLen)159 static 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 
PostParamTrigger(int type,const char * name,const char * value)166 void 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 
PostTrigger(EventType type,const char * content,uint32_t contentLen)181 void 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 
GetTriggerType(const char * type)187 static 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 
GetCommandInfo(const char * cmdLine,int * cmdKeyIndex,char ** content)207 static 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 
ParseJobHookExecute(const char * name,const cJSON * jobNode)222 static 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 
ParseTrigger_(const TriggerWorkSpace * workSpace,const cJSON * triggerItem,int (* checkJobValid)(const char * jobName),const ConfigContext * cfgContext)232 static 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 
ParseTriggerConfig(const cJSON * fileRoot,int (* checkJobValid)(const char * jobName),void * context)275 int 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 
CheckAndMarkTrigger(int type,const char * name)298 int 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 
InitTriggerWorkSpace(void)307 int 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 
CloseTriggerWorkSpace(void)331 void 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 
GetTriggerWorkSpace(void)344 TriggerWorkSpace *GetTriggerWorkSpace(void)
345 {
346     return &g_triggerWorkSpace;
347 }
348 
RegisterTriggerExec(int type,int32_t (* executeTrigger)(const struct tagTriggerNode_ *,const char *,uint32_t))349 void 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 
DoTriggerExec(const char * triggerName)358 void 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 
DoJobExecNow(const char * triggerName)372 void 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 
AddCompleteJob(const char * name,const char * condition,const char * cmdContent)385 int 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 
RegisterBootStateChange(void (* bootStateChange)(int,const char *))408 void RegisterBootStateChange(void (*bootStateChange)(int, const char *))
409 {
410     if (bootStateChange != NULL) {
411         g_triggerWorkSpace.bootStateChange = bootStateChange;
412     }
413 }
414