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
16#include <unistd.h>
17#include <linux/reboot.h>
18#include <sys/reboot.h>
19#include <sys/syscall.h>
20
21#include "reboot_adp.h"
22#include "init_cmdexecutor.h"
23#include "init_module_engine.h"
24#include "init_utils.h"
25#include "plugin_adapter.h"
26#include "securec.h"
27
28#define BUFF_SIZE 256
29#define POWEROFF_REASON_DEV_PATH    "/proc/poweroff_reason"
30
31static int WritePowerOffReason(const char* reason)
32{
33    PLUGIN_CHECK(reason != NULL, return -1, "WritePowerOffReason: reason is NULL\n");
34    PLUGIN_CHECK(access(POWEROFF_REASON_DEV_PATH, F_OK) == 0, return -1,
35                "WritePowerOffReason: access %s failed, errno = %d, %s\n",
36                POWEROFF_REASON_DEV_PATH, errno, strerror(errno));
37    int fd = open(POWEROFF_REASON_DEV_PATH, O_RDWR);
38    PLUGIN_CHECK(fd > 0, return -1, "WritePowerOffReason: errno = %d, %s\n", errno, strerror(errno));
39    int writeBytes = strlen(reason);
40    int ret = write(fd, reason, writeBytes);
41    PLUGIN_CHECK(ret == writeBytes, writeBytes = -1, "WritePowerOffReason: write poweroff reason failed\n");
42    close(fd);
43    return writeBytes;
44}
45
46static void ParseRebootReason(const char *name, int argc, const char **argv)
47{
48    char str[BUFF_SIZE] = {0};
49    int len = sizeof(str);
50    char *tmp = str;
51    int ret;
52    for (int i = 0; i < argc; i++) {
53        ret = sprintf_s(tmp, len - 1, "%s ", argv[i]);
54        if (ret <= 0) {
55            PLUGIN_LOGW("ParseRebootReason: sprintf_s arg %s failed!", argv[i]);
56            break;
57        }
58        len -= ret;
59        tmp += ret;
60    }
61    ret = WritePowerOffReason(str);
62    PLUGIN_CHECK(ret >= 0, return, "ParseRebootReason: write poweroff reason failed\n");
63}
64
65PLUGIN_STATIC int DoRoot_(const char *jobName, int type)
66{
67    // by job to stop service and unmount
68    if (jobName != NULL) {
69        DoJobNow(jobName);
70    }
71#ifndef STARTUP_INIT_TEST
72    return reboot(type);
73#else
74    return 0;
75#endif
76}
77
78static int DoReboot(int id, const char *name, int argc, const char **argv)
79{
80    UNUSED(id);
81    ParseRebootReason(name, argc, argv);
82    // clear misc
83    (void)UpdateMiscMessage(NULL, "reboot", NULL, NULL);
84    return DoRoot_("reboot", RB_AUTOBOOT);
85}
86
87static int DoRebootPanic(int id, const char *name, int argc, const char **argv)
88{
89    UNUSED(id);
90    ParseRebootReason(name, argc, argv);
91    if (InRescueMode() == 0) {
92        PLUGIN_LOGI("Don't panic in resuce mode!");
93        return 0;
94    }
95    // clear misc
96    (void)UpdateMiscMessage(NULL, "reboot", NULL, NULL);
97    DoJobNow("reboot");
98#ifndef STARTUP_INIT_TEST
99    FILE *panic = fopen("/proc/sysrq-trigger", "wb");
100    if (panic == NULL) {
101        return reboot(RB_AUTOBOOT);
102    }
103    if (fwrite((void *)"c", 1, 1, panic) != 1) {
104        (void)fclose(panic);
105        PLUGIN_LOGI("fwrite to panic failed");
106        return -1;
107    }
108    (void)fclose(panic);
109#endif
110    return 0;
111}
112
113PLUGIN_STATIC int DoRebootShutdown(int id, const char *name, int argc, const char **argv)
114{
115    UNUSED(id);
116    PLUGIN_CHECK(argc >= 1, return -1, "Invalid parameter");
117    ParseRebootReason(name, argc, argv);
118    // clear misc
119    (void)UpdateMiscMessage(NULL, "reboot", NULL, NULL);
120    const size_t len = strlen("reboot,");
121    const char *cmd = strstr(argv[0], "reboot,");
122    if (cmd != NULL && strlen(cmd) > len) {
123        PLUGIN_LOGI("DoRebootShutdown argv %s", cmd + len);
124        // by job to stop service and unmount
125        DoJobNow("reboot");
126#ifndef STARTUP_INIT_TEST
127        return syscall(__NR_reboot,
128            LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, cmd + len);
129#else
130        return 0;
131#endif
132    }
133    return DoRoot_("reboot", RB_POWER_OFF);
134}
135
136static int DoRebootUpdater(int id, const char *name, int argc, const char **argv)
137{
138    UNUSED(id);
139    PLUGIN_LOGI("DoRebootUpdater argc %d %s", argc, name);
140    PLUGIN_CHECK(argc >= 1, return -1, "Invalid parameter");
141    PLUGIN_LOGI("DoRebootUpdater argv %s", argv[0]);
142    ParseRebootReason(name, argc, argv);
143    int ret = UpdateMiscMessage(argv[0], "updater", "updater:", "boot_updater");
144    if (ret == 0) {
145        return DoRoot_("reboot", RB_AUTOBOOT);
146    }
147    return ret;
148}
149
150PLUGIN_STATIC int DoRebootFlashed(int id, const char *name, int argc, const char **argv)
151{
152    UNUSED(id);
153    PLUGIN_LOGI("DoRebootFlashed argc %d %s", argc, name);
154    PLUGIN_CHECK(argc >= 1, return -1, "Invalid parameter");
155    PLUGIN_LOGI("DoRebootFlashd argv %s", argv[0]);
156    ParseRebootReason(name, argc, argv);
157    int ret = UpdateMiscMessage(argv[0], "flash", "flash:", "boot_flash");
158    if (ret == 0) {
159        return DoRoot_("reboot", RB_AUTOBOOT);
160    }
161    return ret;
162}
163
164PLUGIN_STATIC int DoRebootCharge(int id, const char *name, int argc, const char **argv)
165{
166    UNUSED(id);
167    ParseRebootReason(name, argc, argv);
168    int ret = UpdateMiscMessage(NULL, "charge", "charge:", "boot_charge");
169    if (ret == 0) {
170        return DoRoot_("reboot", RB_AUTOBOOT);
171    }
172    return ret;
173}
174
175static int DoRebootSuspend(int id, const char *name, int argc, const char **argv)
176{
177    UNUSED(id);
178    ParseRebootReason(name, argc, argv);
179    return DoRoot_("suspend", RB_AUTOBOOT);
180}
181
182PLUGIN_STATIC int DoRebootOther(int id, const char *name, int argc, const char **argv)
183{
184    UNUSED(id);
185    PLUGIN_CHECK(argc >= 1, return -1, "Invalid parameter argc %d", argc);
186    const char *cmd = strstr(argv[0], "reboot,");
187    PLUGIN_CHECK(cmd != NULL, return -1, "Invalid parameter argc %s", argv[0]);
188    PLUGIN_LOGI("DoRebootOther argv %s", argv[0]);
189    ParseRebootReason(name, argc, argv);
190    // clear misc
191    (void)UpdateMiscMessage(NULL, "reboot", NULL, NULL);
192    DoJobNow("reboot");
193#ifndef STARTUP_INIT_TEST
194    return syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
195        LINUX_REBOOT_CMD_RESTART2, cmd + strlen("reboot,"));
196#else
197    return 0;
198#endif
199}
200
201static void RebootAdpInit(void)
202{
203    // add default reboot cmd
204    (void)AddCmdExecutor("reboot", DoReboot);
205    (void)AddCmdExecutor("reboot.other", DoRebootOther);
206    AddRebootCmdExecutor("shutdown", DoRebootShutdown);
207    AddRebootCmdExecutor("flashd", DoRebootFlashed);
208    AddRebootCmdExecutor("updater", DoRebootUpdater);
209    AddRebootCmdExecutor("charge", DoRebootCharge);
210    AddRebootCmdExecutor("suspend", DoRebootSuspend);
211    AddRebootCmdExecutor("panic", DoRebootPanic);
212    (void)AddCmdExecutor("panic", DoRebootPanic);
213}
214
215MODULE_CONSTRUCTOR(void)
216{
217    PLUGIN_LOGI("Reboot adapter plug-in init now ...");
218    RebootAdpInit();
219}
220
221MODULE_DESTRUCTOR(void)
222{
223    PLUGIN_LOGI("Reboot adapter plug-in exit now ...");
224}
225