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
16#include <dirent.h>
17#include <dlfcn.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <linux/limits.h>
22
23#include "beget_ext.h"
24#include "config_policy_utils.h"
25#include "init_utils.h"
26#include "list.h"
27#include "securec.h"
28#include "modulemgr.h"
29
30#define MODULE_SUFFIX_D ".z.so"
31#ifdef SUPPORT_64BIT
32#define MODULE_LIB_NAME "lib64"
33#else
34#define MODULE_LIB_NAME "lib"
35#endif
36#define LIB_NAME_LEN 3
37
38struct tagMODULE_MGR {
39    ListNode modules;
40    const char *name;
41    MODULE_INSTALL_ARGS installArgs;
42};
43
44MODULE_MGR *ModuleMgrCreate(const char *name)
45{
46    MODULE_MGR *moduleMgr;
47
48    BEGET_CHECK(name != NULL, return NULL);
49
50    moduleMgr = (MODULE_MGR *)malloc(sizeof(MODULE_MGR));
51    BEGET_CHECK(moduleMgr != NULL, return NULL);
52    OH_ListInit(&(moduleMgr->modules));
53    moduleMgr->name = strdup(name);
54    if (moduleMgr->name == NULL) {
55        free((void *)moduleMgr);
56        return NULL;
57    }
58    moduleMgr->installArgs.argc = 0;
59    moduleMgr->installArgs.argv = NULL;
60
61    return moduleMgr;
62}
63
64void ModuleMgrDestroy(MODULE_MGR *moduleMgr)
65{
66    BEGET_CHECK(moduleMgr != NULL, return);
67
68    ModuleMgrUninstall(moduleMgr, NULL);
69    BEGET_CHECK(moduleMgr->name == NULL, free((void *)moduleMgr->name));
70    free((void *)moduleMgr);
71}
72
73/*
74 * Module Item related api
75 */
76
77
78typedef struct tagMODULE_ITEM {
79    ListNode node;
80    MODULE_MGR *moduleMgr;
81    const char *name;
82    void *handle;
83} MODULE_ITEM;
84
85static void ModuleDestroy(ListNode *node)
86{
87    MODULE_ITEM *module;
88
89    BEGET_CHECK(node != NULL, return);
90
91    module = (MODULE_ITEM *)node;
92    BEGET_CHECK(module->name == NULL, free((void *)module->name));
93    BEGET_CHECK(module->handle == NULL, dlclose(module->handle));
94    free((void *)module);
95}
96
97static MODULE_INSTALL_ARGS *currentInstallArgs = NULL;
98
99static void *ModuleInstall(MODULE_ITEM *module, int argc, const char *argv[])
100{
101    void *handle;
102    char path[PATH_MAX];
103    int rc;
104
105    module->moduleMgr->installArgs.argc = argc;
106    module->moduleMgr->installArgs.argv = argv;
107
108    BEGET_LOGV("Module install name %s", module->name);
109    if (module->name[0] == '/') {
110        rc = snprintf_s(path, sizeof(path), sizeof(path) - 1, STARTUP_INIT_UT_PATH"%s" MODULE_SUFFIX_D, module->name);
111        BEGET_CHECK(rc >= 0, return NULL);
112    } else {
113        const char *fmt = (InUpdaterMode() == 0) ? "/system/" MODULE_LIB_NAME : "/" MODULE_LIB_NAME;
114        rc = snprintf_s(path, sizeof(path), sizeof(path) - 1,
115            STARTUP_INIT_UT_PATH"%s/%s/lib%s" MODULE_SUFFIX_D, fmt, module->moduleMgr->name, module->name);
116        BEGET_CHECK(rc >= 0, return NULL);
117    }
118    BEGET_LOGV("Module install path %s", path);
119    char *realPath = GetRealPath(path);
120    BEGET_ERROR_CHECK(realPath != NULL, return NULL, "Failed to get real path");
121    currentInstallArgs = &(module->moduleMgr->installArgs);
122    handle = dlopen(realPath, RTLD_LAZY | RTLD_GLOBAL);
123    currentInstallArgs = NULL;
124    BEGET_CHECK_ONLY_ELOG(handle != NULL, "ModuleInstall path %s fail %d", realPath, errno);
125    free(realPath);
126    return handle;
127}
128
129static int ModuleCompare(ListNode *node, void *data)
130{
131    MODULE_ITEM *module = (MODULE_ITEM *)node;
132    const char *name = module->name;
133    if (module->name[0] == '/') {
134        name = strrchr((name), '/') + 1;
135    }
136    if (strncmp(name, "lib", LIB_NAME_LEN) == 0) {
137        name = name + LIB_NAME_LEN;
138    }
139    return strcmp(name, (char *)data);
140}
141
142/*
143 * 用于扫描安装指定目录下所有的插件。
144 */
145int ModuleMgrInstall(MODULE_MGR *moduleMgr, const char *moduleName,
146                     int argc, const char *argv[])
147{
148    MODULE_ITEM *module;
149    BEGET_LOGV("ModuleMgrInstall moduleName %s", moduleName);
150    // Get module manager
151    BEGET_CHECK(!(moduleMgr == NULL || moduleName == NULL), return -1);
152
153    module = (MODULE_ITEM *)OH_ListFind(&(moduleMgr->modules), (void *)moduleName, ModuleCompare);
154    BEGET_ERROR_CHECK(module == NULL, return 0, "%s module already exists", moduleName);
155
156    // Create module item
157    module = (MODULE_ITEM *)malloc(sizeof(MODULE_ITEM));
158    BEGET_CHECK(module != NULL, return -1);
159
160    module->handle = NULL;
161    module->moduleMgr = moduleMgr;
162
163    module->name = strdup(moduleName);
164    BEGET_CHECK(module->name != NULL, free(module);
165        return -1);
166
167    // Install
168    module->handle = ModuleInstall(module, argc, argv);
169#ifndef STARTUP_INIT_TEST
170    if (module->handle == NULL) {
171        BEGET_LOGE("Failed to install module %s", module->name);
172        ModuleDestroy((ListNode *)module);
173        return -1;
174    }
175#endif
176    // Add to list
177    OH_ListAddTail(&(moduleMgr->modules), (ListNode *)module);
178
179    return 0;
180}
181
182const MODULE_INSTALL_ARGS *ModuleMgrGetArgs(void)
183{
184    return currentInstallArgs;
185}
186
187static int StringEndsWith(const char *srcStr, const char *endStr)
188{
189    int srcStrLen = strlen(srcStr);
190    int endStrLen = strlen(endStr);
191
192    BEGET_CHECK(!(srcStrLen < endStrLen), return -1);
193
194    srcStr += (srcStrLen - endStrLen);
195    BEGET_CHECK(strcmp(srcStr, endStr) != 0, return (srcStrLen - endStrLen));
196    return -1;
197}
198
199static void ScanModules(MODULE_MGR *moduleMgr, const char *path)
200{
201    BEGET_LOGV("Scan module with name '%s'", path);
202    DIR *dir = opendir(path);
203    BEGET_CHECK(dir != NULL, return);
204    char *moduleName = calloc(PATH_MAX, sizeof(char));
205    while (moduleName != NULL) {
206        struct dirent *file = readdir(dir);
207        if (file == NULL) {
208            break;
209        }
210        if ((file->d_type != DT_REG) && (file->d_type != DT_LNK)) {
211            continue;
212        }
213
214        // Must be ended with MODULE_SUFFIX_D
215        int end = StringEndsWith(file->d_name, MODULE_SUFFIX_D);
216        if (end <= 0) {
217            continue;
218        }
219
220        file->d_name[end] = '\0';
221        int len = sprintf_s(moduleName, PATH_MAX - 1, "%s/%s", path, file->d_name);
222        if (len > 0) {
223            moduleName[len] = '\0';
224            BEGET_LOGI("Scan module with name '%s'", moduleName);
225            ModuleMgrInstall(moduleMgr, moduleName, 0, NULL);
226        }
227    }
228    if (moduleName != NULL) {
229        free(moduleName);
230    }
231    closedir(dir);
232}
233
234/*
235 * 用于扫描安装指定目录下所有的插件。
236 */
237MODULE_MGR *ModuleMgrScan(const char *modulePath)
238{
239    MODULE_MGR *moduleMgr;
240    char path[PATH_MAX];
241    BEGET_LOGV("ModuleMgrScan moduleName %s", modulePath);
242    moduleMgr = ModuleMgrCreate(modulePath);
243    BEGET_CHECK(moduleMgr != NULL, return NULL);
244
245    if (modulePath[0] == '/') {
246        ScanModules(moduleMgr, modulePath);
247    } else if (InUpdaterMode() == 1) {
248        BEGET_CHECK(snprintf_s(path, sizeof(path), sizeof(path) - 1,
249            "/%s/%s", MODULE_LIB_NAME, modulePath) > 0, free((void *)moduleMgr); return NULL);
250        ScanModules(moduleMgr, path);
251    } else {
252        BEGET_CHECK(snprintf_s(path, sizeof(path), sizeof(path) - 1,
253            "%s/%s", MODULE_LIB_NAME, modulePath) > 0, free((void *)moduleMgr); return NULL);
254        CfgFiles *files = GetCfgFiles(path);
255        for (int i = MAX_CFG_POLICY_DIRS_CNT - 1; files && i >= 0; i--) {
256            if (files->paths[i]) {
257                ScanModules(moduleMgr, files->paths[i]);
258            }
259        }
260        FreeCfgFiles(files);
261    }
262    return moduleMgr;
263}
264
265/*
266 * 卸载指定插件。
267 */
268void ModuleMgrUninstall(MODULE_MGR *moduleMgr, const char *name)
269{
270    MODULE_ITEM *module;
271    BEGET_CHECK(moduleMgr != NULL, return);
272    // Uninstall all modules if no name specified
273    if (name == NULL) {
274        OH_ListRemoveAll(&(moduleMgr->modules), ModuleDestroy);
275        return;
276    }
277    BEGET_LOGV("ModuleMgrUninstall moduleName %s", name);
278    // Find module by name
279    module = (MODULE_ITEM *)OH_ListFind(&(moduleMgr->modules), (void *)name, ModuleCompare);
280    BEGET_ERROR_CHECK(module != NULL, return, "Can not find module %s", name);
281
282    // Remove from the list
283    OH_ListRemove((ListNode *)module);
284    // Destroy the module
285    ModuleDestroy((ListNode *)module);
286}
287
288int ModuleMgrGetCnt(const MODULE_MGR *moduleMgr)
289{
290    BEGET_CHECK(moduleMgr != NULL, return 0);
291    return OH_ListGetCnt(&(moduleMgr->modules));
292}
293
294typedef struct tagMODULE_TRAVERSAL_ARGS {
295    void  *cookie;
296    OhosModuleTraversal traversal;
297} MODULE_TRAVERSAL_ARGS;
298
299static int ModuleTraversalProc(ListNode *node, void *cookie)
300{
301    MODULE_ITEM *module;
302    MODULE_TRAVERSAL_ARGS *args;
303    MODULE_INFO info;
304
305    module = (MODULE_ITEM *)node;
306    args = (MODULE_TRAVERSAL_ARGS *)cookie;
307
308    info.cookie = args->cookie;
309    info.handle = module->handle;
310    info.name = module->name;
311    args->traversal(&info);
312
313    return 0;
314}
315
316/**
317 * @brief Traversing all hooks in the HookManager
318 *
319 * @param moduleMgr HookManager handle.
320 *                If hookMgr is NULL, it will use default HookManager
321 * @param cookie traversal cookie.
322 * @param traversal traversal function.
323 * @return None.
324 */
325void ModuleMgrTraversal(const MODULE_MGR *moduleMgr, void *cookie, OhosModuleTraversal traversal)
326{
327    MODULE_TRAVERSAL_ARGS args;
328    if (moduleMgr == NULL) {
329        return;
330    }
331
332    args.cookie = cookie;
333    args.traversal = traversal;
334    OH_ListTraversal((ListNode *)(&(moduleMgr->modules)), (void *)(&args), ModuleTraversalProc, 0);
335}
336