1/*
2 * Copyright (c) 2021-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 "param_persist.h"
16
17#include <time.h>
18
19#include "init_param.h"
20#include "init_utils.h"
21#include "param_manager.h"
22#include "param_trie.h"
23#include "param_osadp.h"
24#include "securec.h"
25
26static ParamPersistWorkSpace g_persistWorkSpace = {0, 0, NULL, {0}, {0}};
27static int IsNeedToSave(const char *name)
28{
29#if defined(__LITEOS_M__) || defined(__LITEOS_A__)
30    return IS_READY_ONLY(name) ? 0 : 1;
31#else
32    return (strncmp(name, PARAM_PERSIST_PREFIX, strlen(PARAM_PERSIST_PREFIX)) == 0) ? 1 : 0;
33#endif
34}
35
36static long long GetPersistCommitId(void)
37{
38    ParamWorkSpace *paramSpace = GetParamWorkSpace();
39    PARAM_CHECK(paramSpace != NULL, return 0, "Invalid paramSpace");
40    PARAM_WORKSPACE_CHECK(paramSpace, return 0, "Invalid space");
41    WorkSpace *space = GetWorkSpace(WORKSPACE_INDEX_DAC);
42    if (space == NULL) {
43        return 0;
44    }
45    PARAMSPACE_AREA_RD_LOCK(space);
46    long long globalCommitId =  ATOMIC_UINT64_LOAD_EXPLICIT(&space->area->commitPersistId, MEMORY_ORDER_ACQUIRE);
47    PARAMSPACE_AREA_RW_UNLOCK(space);
48    return globalCommitId;
49}
50
51static void UpdatePersistCommitId(void)
52{
53    ParamWorkSpace *paramSpace = GetParamWorkSpace();
54    PARAM_CHECK(paramSpace != NULL, return, "Invalid paramSpace");
55    PARAM_WORKSPACE_CHECK(paramSpace, return, "Invalid space");
56    WorkSpace *space = GetWorkSpace(WORKSPACE_INDEX_DAC);
57    if (space == NULL) {
58        return;
59    }
60    PARAMSPACE_AREA_RW_LOCK(space);
61    ATOMIC_SYNC_ADD_AND_FETCH(&space->area->commitPersistId, 1, MEMORY_ORDER_RELEASE);
62    PARAMSPACE_AREA_RW_UNLOCK(space);
63}
64
65static int SavePersistParam(const WorkSpace *workSpace, const ParamTrieNode *node, const void *cookie)
66{
67    ParamTrieNode *current = (ParamTrieNode *)node;
68    if (current == NULL || current->dataIndex == 0) {
69        return 0;
70    }
71    ParamNode *entry = (ParamNode *)GetTrieNode(workSpace, current->dataIndex);
72    if (entry == NULL) {
73        return 0;
74    }
75
76    if (!IsNeedToSave(entry->data)) {
77        return 0;
78    }
79
80    if ((entry->commitId & PARAM_FLAGS_PERSIST) == 0) {
81        return 0;
82    }
83    static char name[PARAM_NAME_LEN_MAX] = {0};
84    int ret = memcpy_s(name, PARAM_NAME_LEN_MAX - 1, entry->data, entry->keyLength);
85    PARAM_CHECK(ret == EOK, return -1, "Failed to read param name %s", entry->data);
86    name[entry->keyLength] = '\0';
87    ret = g_persistWorkSpace.persistParamOps.batchSave(
88        (PERSIST_SAVE_HANDLE)cookie, name, entry->data + entry->keyLength + 1);
89    PARAM_CHECK(ret == 0, return -1, "Failed to write param %s", current->key);
90    return ret;
91}
92
93static int BatchSavePersistParam(void)
94{
95    PARAM_LOGV("BatchSavePersistParam");
96    if (g_persistWorkSpace.persistParamOps.batchSaveBegin == NULL ||
97        g_persistWorkSpace.persistParamOps.batchSave == NULL ||
98        g_persistWorkSpace.persistParamOps.batchSaveEnd == NULL) {
99        return 0;
100    }
101#if defined(__LITEOS_M__) || defined(__LITEOS_A__) || defined(__LINUX__)
102    PERSIST_SAVE_HANDLE handle;
103    int ret = g_persistWorkSpace.persistParamOps.batchSaveBegin(&handle);
104#if defined(__LITEOS_M__) || defined(__LITEOS_A__)
105    const char *prefix = "";
106#else
107    const char *prefix = PARAM_PERSIST_PREFIX;
108#endif
109#else
110    PERSIST_SAVE_HANDLE handle[PERSIST_HANDLE_MAX] = { NULL, NULL };
111    int ret = g_persistWorkSpace.persistParamOps.batchSaveBegin(handle);
112    const char *prefix = PARAM_PERSIST_PREFIX;
113#endif
114    PARAM_CHECK(ret == 0, return PARAM_CODE_INVALID_NAME, "Failed to save persist");
115    // walk and save persist param
116    WorkSpace *workSpace = GetNextWorkSpace(NULL);
117    while (workSpace != NULL) {
118        WorkSpace *next = GetNextWorkSpace(workSpace);
119        ParamTrieNode *root = FindTrieNode(workSpace, prefix, strlen(prefix), NULL);
120        PARAMSPACE_AREA_RD_LOCK(workSpace);
121        TraversalTrieNode(workSpace, root, SavePersistParam, (void *)handle);
122        PARAMSPACE_AREA_RW_UNLOCK(workSpace);
123        workSpace = next;
124    }
125    g_persistWorkSpace.persistParamOps.batchSaveEnd(handle);
126    PARAM_CHECK(ret == 0, return PARAM_CODE_INVALID_NAME, "Save persist param fail");
127
128    PARAM_CLEAR_FLAG(g_persistWorkSpace.flags, WORKSPACE_FLAGS_UPDATE);
129    (void)clock_gettime(CLOCK_MONOTONIC, &g_persistWorkSpace.lastSaveTimer);
130    return ret;
131}
132
133INIT_LOCAL_API int InitPersistParamWorkSpace(void)
134{
135    if (PARAM_TEST_FLAG(g_persistWorkSpace.flags, WORKSPACE_FLAGS_INIT)) {
136        return 0;
137    }
138    (void)clock_gettime(CLOCK_MONOTONIC, &g_persistWorkSpace.lastSaveTimer);
139    RegisterPersistParamOps(&g_persistWorkSpace.persistParamOps);
140    PARAM_SET_FLAG(g_persistWorkSpace.flags, WORKSPACE_FLAGS_INIT);
141    return 0;
142}
143
144INIT_LOCAL_API void ClosePersistParamWorkSpace(void)
145{
146    if (g_persistWorkSpace.saveTimer != NULL) {
147        ParamTimerClose(g_persistWorkSpace.saveTimer);
148    }
149    g_persistWorkSpace.flags = 0;
150}
151
152void CheckAndSavePersistParam(void)
153{
154    // check commit
155    long long commit = GetPersistCommitId();
156    PARAM_LOGV("CheckAndSavePersistParam commit %lld %lld", commit, g_persistWorkSpace.commitId);
157    if (g_persistWorkSpace.commitId == commit) {
158        return;
159    }
160    g_persistWorkSpace.commitId = commit;
161    (void)BatchSavePersistParam();
162}
163
164PARAM_STATIC void TimerCallbackForSave(const ParamTaskPtr timer, void *context)
165{
166    UNUSED(context);
167    UNUSED(timer);
168    PARAM_LOGV("TimerCallbackForSave ");
169    // for liteos-a must cycle check
170#if (!defined(PARAM_SUPPORT_CYCLE_CHECK) || defined(PARAM_SUPPORT_REAL_CHECK))
171    ParamTimerClose(g_persistWorkSpace.saveTimer);
172    g_persistWorkSpace.saveTimer = NULL;
173    if (!PARAM_TEST_FLAG(g_persistWorkSpace.flags, WORKSPACE_FLAGS_UPDATE)) {
174        return;
175    }
176#endif
177    CheckAndSavePersistParam();
178}
179
180INIT_LOCAL_API int WritePersistParam(const char *name, const char *value)
181{
182    PARAM_CHECK(value != NULL && name != NULL, return PARAM_CODE_INVALID_PARAM, "Invalid param");
183    if (!IsNeedToSave(name)) {
184        return 0;
185    }
186    PARAM_LOGV("WritePersistParam name %s ", name);
187    if (g_persistWorkSpace.persistParamOps.save != NULL) {
188        g_persistWorkSpace.persistParamOps.save(name, value);
189    }
190    // update commit for check
191    UpdatePersistCommitId();
192    // for liteos-m, start task to check and save parameter
193    // for linux, start timer after set persist parameter
194    // for liteos-a, start timer in init to check and save parameter
195#ifdef PARAM_SUPPORT_REAL_CHECK
196    if (!PARAM_TEST_FLAG(g_persistWorkSpace.flags, WORKSPACE_FLAGS_LOADED)) {
197        PARAM_LOGE("Can not save persist param before load %s ", name);
198        return 0;
199    }
200    if (g_persistWorkSpace.persistParamOps.batchSave == NULL) {
201        return 0;
202    }
203
204    // check timer for save all
205    struct timespec currTimer = {0};
206    (void)clock_gettime(CLOCK_MONOTONIC, &currTimer);
207    uint32_t diff = IntervalTime(&g_persistWorkSpace.lastSaveTimer, &currTimer);
208    if (diff > PARAM_MUST_SAVE_PARAM_DIFF) {
209        if (g_persistWorkSpace.saveTimer != NULL) {
210            ParamTimerClose(g_persistWorkSpace.saveTimer);
211            g_persistWorkSpace.saveTimer = NULL;
212        }
213        return BatchSavePersistParam();
214    }
215
216    PARAM_SET_FLAG(g_persistWorkSpace.flags, WORKSPACE_FLAGS_UPDATE);
217    if (g_persistWorkSpace.saveTimer == NULL) {
218        ParamTimerCreate(&g_persistWorkSpace.saveTimer, TimerCallbackForSave, NULL);
219        ParamTimerStart(g_persistWorkSpace.saveTimer, PARAM_MUST_SAVE_PARAM_DIFF * MS_UNIT, MS_UNIT);
220    }
221#endif
222    return 0;
223}
224
225int LoadPersistParams(void)
226{
227    // get persist parameter
228    int ret = InitPersistParamWorkSpace();
229    PARAM_CHECK(ret == 0, return ret, "Failed to init persist param");
230#ifndef STARTUP_INIT_TEST
231    if (PARAM_TEST_FLAG(g_persistWorkSpace.flags, WORKSPACE_FLAGS_LOADED)) {
232        PARAM_LOGE("Persist param has been loaded");
233        return 0;
234    }
235#endif
236#if defined(__LITEOS_M__) || defined(__LITEOS_A__) || defined(__LINUX__)
237    if (g_persistWorkSpace.persistParamOps.load != NULL) {
238        (void)g_persistWorkSpace.persistParamOps.load();
239        PARAM_SET_FLAG(g_persistWorkSpace.flags, WORKSPACE_FLAGS_LOADED);
240    }
241    // save new persist param
242    ret = BatchSavePersistParam();
243    PARAM_CHECK(ret == 0, return ret, "Failed to load persist param");
244    // for liteos-a, start time to check in init
245#ifdef PARAM_SUPPORT_CYCLE_CHECK
246    PARAM_LOGV("LoadPersistParams start check time ");
247    if (g_persistWorkSpace.saveTimer == NULL) {
248        ParamTimerCreate(&g_persistWorkSpace.saveTimer, TimerCallbackForSave, NULL);
249        ParamTimerStart(g_persistWorkSpace.saveTimer, PARAM_MUST_SAVE_PARAM_DIFF * MS_UNIT, MS_UNIT);
250    }
251#endif
252#else
253    if (g_persistWorkSpace.persistParamOps.load != NULL) {
254        (void)g_persistWorkSpace.persistParamOps.load(PUBLIC_PERSIST_FILE);
255    }
256#endif
257    return 0;
258}
259
260int LoadPrivatePersistParams(void)
261{
262#if !(defined(__LITEOS_M__) || defined(__LITEOS_A__) || defined(__LINUX__))
263#ifndef STARTUP_INIT_TEST
264    if (PARAM_TEST_FLAG(g_persistWorkSpace.flags, WORKSPACE_FLAGS_LOADED)) {
265        PARAM_LOGE("Persist param has been loaded");
266        return 0;
267    }
268#endif
269    if (g_persistWorkSpace.persistParamOps.load != NULL) {
270        (void)g_persistWorkSpace.persistParamOps.load(PRIVATE_PERSIST_FILE);
271        PARAM_SET_FLAG(g_persistWorkSpace.flags, WORKSPACE_FLAGS_LOADED);
272    }
273    // save new persist param
274    int ret = BatchSavePersistParam();
275    PARAM_CHECK(ret == 0, return ret, "Failed to load persist param");
276    // for liteos-a, start time to check in init
277#ifdef PARAM_SUPPORT_CYCLE_CHECK
278    PARAM_LOGV("LoadPersistParams start check time ");
279    if (g_persistWorkSpace.saveTimer == NULL) {
280        ParamTimerCreate(&g_persistWorkSpace.saveTimer, TimerCallbackForSave, NULL);
281        ParamTimerStart(g_persistWorkSpace.saveTimer, PARAM_MUST_SAVE_PARAM_DIFF * MS_UNIT, MS_UNIT);
282    }
283#endif
284#endif
285    return 0;
286}