1/*
2 * Copyright (c) 2022 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 <errno.h>
16
17#include "bootstage.h"
18#include "hookmgr.h"
19#include "init_hook.h"
20#include "init_module_engine.h"
21#include "plugin_adapter.h"
22#include "securec.h"
23
24#define REBOOT_NAME_PREFIX "reboot,"
25#define REBOOT_CMD_PREFIX "reboot."
26#define REBOOT_REPLACE_PREFIX "reboot."
27#define PARAM_CMD_MAX 100
28
29static int RebootHookWrapper(const HOOK_INFO *hookInfo, void *executionContext)
30{
31    RebootHookCtx *ctx = (RebootHookCtx *)executionContext;
32    InitRebootHook realHook = (InitRebootHook)hookInfo->hookCookie;
33    realHook(ctx);
34    return 0;
35};
36
37int InitAddRebootHook(InitRebootHook hook)
38{
39    HOOK_INFO info;
40    info.stage = INIT_REBOOT;
41    info.prio = 0;
42    info.hook = RebootHookWrapper;
43    info.hookCookie = (void *)hook;
44    return HookMgrAddEx(GetBootStageHookMgr(), &info);
45}
46
47static ParamCmdInfo *g_rebootParamCmdInfos = NULL;
48static int g_rebootParamCmdMaxNumber = 0;
49static int g_rebootParamCmdValidNumber = 0;
50static char *Dup2String(const char *prefix, const char *str)
51{
52    if (str == NULL) {
53        char *tmpstr = strdup("reboot");
54        PLUGIN_CHECK(tmpstr != NULL, return NULL, "Failed to get str");
55        return tmpstr;
56    }
57    size_t len = strlen(prefix) + strlen(str) + 1;
58    char *tmp = calloc(1, len);
59    PLUGIN_CHECK(tmp != NULL, return NULL, "Failed to alloc %s %s", prefix, str);
60    int ret = sprintf_s(tmp, len, "%s%s", prefix, str);
61    PLUGIN_CHECK(ret > 0, free(tmp);
62        return NULL, "Failed to sprintf %s %s", prefix, str);
63    return tmp;
64}
65
66static int CheckParamCmdExist(const char *cmd)
67{
68    if (g_rebootParamCmdInfos == NULL) {
69        return 0;
70    }
71    char *cmdName = Dup2String(REBOOT_CMD_PREFIX, cmd);
72    PLUGIN_CHECK(cmdName != NULL, return 0, "Failed to copy %s", cmd);
73    for (int i = 0; i < g_rebootParamCmdValidNumber; i++) {
74        if (strcmp(g_rebootParamCmdInfos[i].cmd, cmdName) == 0) {
75            free(cmdName);
76            return 1;
77        }
78    }
79    free(cmdName);
80    return 0;
81}
82
83static int SetParamCmdInfo(ParamCmdInfo *currInfo, CmdExecutor executor, const char *cmd)
84{
85    do {
86        currInfo->name = Dup2String(REBOOT_NAME_PREFIX, cmd);
87        PLUGIN_CHECK(currInfo->name != NULL, break, "Failed to copy %s", cmd);
88        currInfo->replace = Dup2String(REBOOT_REPLACE_PREFIX, cmd);
89        PLUGIN_CHECK(currInfo->replace != NULL, break, "Failed to copy %s", cmd);
90        currInfo->cmd = Dup2String(REBOOT_CMD_PREFIX, cmd);
91        PLUGIN_CHECK(currInfo->cmd != NULL, break, "Failed to copy %s", cmd);
92        if (executor != NULL) {
93            int cmdId = AddCmdExecutor(currInfo->cmd, executor);
94            PLUGIN_CHECK(cmdId > 0, break, "Failed to add cmd %s", cmd);
95        }
96        PLUGIN_LOGV("SetParamCmdInfo '%s' '%s' '%s' ", currInfo->name, currInfo->cmd, currInfo->replace);
97        currInfo = NULL;
98        g_rebootParamCmdValidNumber++;
99        return 0;
100    } while (0);
101    if (currInfo != NULL) {
102        if (currInfo->name != NULL) {
103            free(currInfo->name);
104        }
105        if (currInfo->cmd != NULL) {
106            free(currInfo->cmd);
107        }
108        if (currInfo->replace != NULL) {
109            free(currInfo->replace);
110        }
111    }
112    return -1;
113}
114
115static int AddRebootCmdExecutor_(const char *cmd, CmdExecutor executor)
116{
117    PLUGIN_CHECK(g_rebootParamCmdMaxNumber <= PARAM_CMD_MAX, return -1, "Param cmd max number exceed limit");
118    if (g_rebootParamCmdMaxNumber == 0 || g_rebootParamCmdMaxNumber <= g_rebootParamCmdValidNumber) {
119        g_rebootParamCmdMaxNumber += 5; // inc 5 once time
120        ParamCmdInfo *cmdInfos = calloc(1, sizeof(ParamCmdInfo) * g_rebootParamCmdMaxNumber);
121        PLUGIN_CHECK(cmdInfos != NULL, return -1, "Failed to add reboot cmd %s", cmd);
122        if (g_rebootParamCmdInfos != NULL) { // delete old
123            // copy from old
124            for (int i = 0; i < g_rebootParamCmdValidNumber; i++) {
125                cmdInfos[i].name = g_rebootParamCmdInfos[i].name;
126                cmdInfos[i].replace = g_rebootParamCmdInfos[i].replace;
127                cmdInfos[i].cmd = g_rebootParamCmdInfos[i].cmd;
128            }
129            free(g_rebootParamCmdInfos);
130        }
131        g_rebootParamCmdInfos = cmdInfos;
132    }
133    PLUGIN_CHECK(g_rebootParamCmdValidNumber >= 0 && g_rebootParamCmdValidNumber < g_rebootParamCmdMaxNumber,
134        return -1, "Param cmd number exceed limit");
135    return SetParamCmdInfo(&g_rebootParamCmdInfos[g_rebootParamCmdValidNumber], executor, cmd);
136}
137
138int AddRebootCmdExecutor(const char *cmd, CmdExecutor executor)
139{
140    PLUGIN_CHECK(cmd != NULL && executor != NULL, return EINVAL, "Invalid input parameter");
141    int ret = CheckParamCmdExist(cmd);
142    if (ret != 0) {
143        PLUGIN_LOGI("Cmd %s exist", cmd);
144        return EEXIST;
145    }
146    return AddRebootCmdExecutor_(cmd, executor);
147}
148
149const ParamCmdInfo *GetStartupPowerCtl(size_t *size)
150{
151    RebootHookCtx context;
152    context.reason = "";
153    (void)HookMgrExecute(GetBootStageHookMgr(), INIT_REBOOT, (void *)(&context), NULL);
154    PLUGIN_LOGI("After install reboot module");
155    *size = g_rebootParamCmdValidNumber;
156    return g_rebootParamCmdInfos;
157}
158
159static void InitRebootHook_(RebootHookCtx *ctx)
160{
161#ifndef STARTUP_INIT_TEST // do not install
162    ModuleMgrScan("init/reboot");
163#endif
164    PLUGIN_LOGI("Install rebootmodule.");
165}
166
167MODULE_CONSTRUCTOR(void)
168{
169    // 执行reboot时调用,安装reboot模块
170    InitAddRebootHook(InitRebootHook_);
171}
172
173MODULE_DESTRUCTOR(void)
174{
175    for (int i = 0; i < g_rebootParamCmdValidNumber; i++) {
176        if (g_rebootParamCmdInfos[i].name != NULL) {
177            free(g_rebootParamCmdInfos[i].name);
178        }
179        if (g_rebootParamCmdInfos[i].replace != NULL) {
180            free(g_rebootParamCmdInfos[i].replace);
181        }
182        if (g_rebootParamCmdInfos[i].cmd != NULL) {
183            free(g_rebootParamCmdInfos[i].cmd);
184        }
185    }
186    free(g_rebootParamCmdInfos);
187    g_rebootParamCmdInfos = NULL;
188}