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#include <limits.h>
17#include <stdlib.h>
18#include <sys/mount.h>
19
20#include "reboot_adp.h"
21#include "fs_manager/fs_manager.h"
22#include "init_utils.h"
23#include "securec.h"
24
25#define MAX_COMMAND_SIZE 32
26#define MAX_UPDATE_SIZE 1280
27#define MAX_RESERVED_SIZE 736
28
29struct RBMiscUpdateMessage {
30    char command[MAX_COMMAND_SIZE];
31    char update[MAX_UPDATE_SIZE];
32    char reserved[MAX_RESERVED_SIZE];
33};
34
35static int RBMiscWriteUpdaterMessage(const char *path, const struct RBMiscUpdateMessage *boot)
36{
37    char *realPath = GetRealPath(path);
38    BEGET_CHECK_RETURN_VALUE(realPath != NULL, -1);
39    int ret = -1;
40    FILE *fp = fopen(realPath, "rb+");
41    free(realPath);
42    realPath = NULL;
43    if (fp != NULL) {
44        size_t writeLen = fwrite(boot, sizeof(struct RBMiscUpdateMessage), 1, fp);
45        BEGET_ERROR_CHECK(writeLen == 1, ret = -1, "Failed to write misc for reboot");
46        (void)fclose(fp);
47        ret = 0;
48    }
49    return ret;
50}
51
52static int RBMiscReadUpdaterMessage(const char *path, struct RBMiscUpdateMessage *boot)
53{
54    int ret = -1;
55    FILE *fp = NULL;
56    char *realPath = GetRealPath(path);
57    if (realPath != NULL) {
58        fp = fopen(realPath, "rb");
59        free(realPath);
60        realPath = NULL;
61    } else {
62        fp = fopen(path, "rb");
63    }
64    if (fp != NULL) {
65        size_t readLen = fread(boot, 1, sizeof(struct RBMiscUpdateMessage), fp);
66        (void)fclose(fp);
67        BEGET_ERROR_CHECK(readLen > 0, ret = -1, "Failed to read misc for reboot");
68        ret = 0;
69    }
70    return ret;
71}
72
73int GetRebootReasonFromMisc(char *reason, size_t size)
74{
75    char miscFile[PATH_MAX] = {0};
76    int ret = GetBlockDevicePath("/misc", miscFile, PATH_MAX);
77    BEGET_ERROR_CHECK(ret == 0, return -1, "Failed to get misc path");
78    struct RBMiscUpdateMessage msg;
79    ret = RBMiscReadUpdaterMessage(miscFile, &msg);
80    BEGET_ERROR_CHECK(ret == 0, return -1, "Failed to get misc info");
81    return strcpy_s(reason, size, msg.command);
82}
83
84int UpdateMiscMessage(const char *valueData, const char *cmd, const char *cmdExt, const char *boot)
85{
86    char miscFile[PATH_MAX] = {0};
87    int ret = GetBlockDevicePath("/misc", miscFile, PATH_MAX);
88    // no misc do not updater, so return ok
89    BEGET_ERROR_CHECK(ret == 0, return 0, "Failed to get misc path for %s.", valueData);
90
91    // "updater" or "updater:"
92    struct RBMiscUpdateMessage msg;
93    ret = RBMiscReadUpdaterMessage(miscFile, &msg);
94    BEGET_ERROR_CHECK(ret == 0, return -1, "Failed to get misc info for %s.", cmd);
95
96    if (boot != NULL) {
97        ret = snprintf_s(msg.command, MAX_COMMAND_SIZE, MAX_COMMAND_SIZE - 1, "%s", boot);
98        BEGET_ERROR_CHECK(ret > 0, return -1, "Failed to format cmd for %s.", cmd);
99        msg.command[MAX_COMMAND_SIZE - 1] = 0;
100    } else {
101        ret = memset_s(msg.command, MAX_COMMAND_SIZE, 0, MAX_COMMAND_SIZE);
102        BEGET_ERROR_CHECK(ret == 0, return -1, "Failed to format cmd for %s.", cmd);
103    }
104
105    if (strncmp(cmd, "updater", strlen("updater")) != 0) {
106        if ((cmdExt != NULL) && (valueData != NULL) && (strncmp(valueData, cmdExt, strlen(cmdExt)) == 0)) {
107            const char *p = valueData + strlen(cmdExt);
108            ret = snprintf_s(msg.update, MAX_UPDATE_SIZE, MAX_UPDATE_SIZE - 1, "%s", p);
109            BEGET_ERROR_CHECK(ret > 0, return -1, "Failed to format param for %s.", cmd);
110            msg.update[MAX_UPDATE_SIZE - 1] = 0;
111        } else {
112            ret = memset_s(msg.update, MAX_UPDATE_SIZE, 0, MAX_UPDATE_SIZE);
113            BEGET_ERROR_CHECK(ret == 0, return -1, "Failed to format update for %s.", cmd);
114        }
115    }
116
117    if (RBMiscWriteUpdaterMessage(miscFile, &msg) == 0) {
118        return 0;
119    }
120    return -1;
121}
122