• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 #include <cerrno>
17 #include <dlfcn.h>
18 #include <fcntl.h>
19 #include <sys/signalfd.h>
20 #include <sys/wait.h>
21 
22 #include <algorithm>
23 #include <ctime>
24 #include <map>
25 #include <mutex>
26 #include <string>
27 
28 #ifdef __MUSL__
29 #include <dlfcn_ext.h>
30 #include <cerrno>
31 #include <sys/mman.h>
32 #endif
33 
34 #include "appspawn_service.h"
35 #include "appspawn_adapter.h"
36 
37 #ifdef WITH_SECCOMP
38 #include "seccomp_policy.h"
39 #endif
40 
41 struct RenderProcessNode {
RenderProcessNodeRenderProcessNode42     RenderProcessNode(time_t now, int exit):recordTime_(now), exitStatus_(exit) {}
43     time_t recordTime_;
44     int exitStatus_;
45 };
46 
47 namespace {
48     constexpr int32_t RENDER_PROCESS_MAX_NUM = 16;
49     constexpr int32_t RETRY_MAX_TIMES = 60;
50     constexpr int32_t WAIT_NWEB_LIB_MARGIN = 5;
51     std::map<int32_t, RenderProcessNode> g_renderProcessMap;
52     std::mutex g_mutex;
53     void *g_nwebRenderHandle = nullptr;
54 }
55 
GetNWebHapLibsPath()56 std::string GetNWebHapLibsPath()
57 {
58     APPSPAWN_LOGI("GetNWebHapLibsPath");
59 
60 #if defined(webview_arm64)
61     const std::string NWEB_HAP_LIB_PATH = "/data/storage/el1/bundle/nweb/libs/arm64";
62 #elif defined(webview_x86_64)
63     const std::string NWEB_HAP_LIB_PATH = "/data/storage/el1/bundle/nweb/libs/x86_64";
64 #elif defined(webview_arm)
65     const std::string NWEB_HAP_LIB_PATH = "/data/app/el1/bundle/public/com.ohos.nweb/libs/arm";
66 #endif
67 
68 #if defined(webview_arm)
69     std::string nwebLibenginePath = NWEB_HAP_LIB_PATH + "/libweb_engine.so";
70     std::string nwebLibrenderPath = NWEB_HAP_LIB_PATH + "/libnweb_render.so";
71     int retryCnt = 0;
72     do {
73         if ((access(nwebLibenginePath.c_str(), F_OK) == 0) && (access(nwebLibrenderPath.c_str(), F_OK) == 0)) {
74             APPSPAWN_LOGI("get nweb hap lib path success");
75             sleep(WAIT_NWEB_LIB_MARGIN);
76             return NWEB_HAP_LIB_PATH;
77         }
78         APPSPAWN_LOGW("get nweb hap lib path failed, errno = %{public}d, retry times = %{public}d", errno, retryCnt);
79         sleep(1);
80         retryCnt++;
81     } while (retryCnt < RETRY_MAX_TIMES);
82 
83     APPSPAWN_LOGE("get nweb hap lib path failed, errno = %{public}d, retry times = %{public}d", errno, retryCnt);
84     return "";
85 #else
86     return NWEB_HAP_LIB_PATH;
87 #endif
88 }
89 
PreLoadWebEngineLibs()90 void PreLoadWebEngineLibs()
91 {
92     APPSPAWN_LOGI("PreLoadWebengineLibs");
93 
94     void *webEngineHandle = nullptr;
95     std::string webEngineLibsDir = GetNWebHapLibsPath();
96 #ifdef __MUSL__
97     Dl_namespace dlns;
98     dlns_init(&dlns, "nweb_ns");
99     dlns_create(&dlns, webEngineLibsDir.c_str());
100 
101     // preload libweb_engine
102     webEngineHandle = dlopen_ns(&dlns, "libweb_engine.so", RTLD_NOW | RTLD_GLOBAL);
103 
104     // load libnweb_render
105     g_nwebRenderHandle = dlopen_ns(&dlns, "libnweb_render.so", RTLD_NOW | RTLD_GLOBAL);
106 #else
107     // preload libweb_engine
108     const std::string engineLibDir = NWEB_HAP_LIB_PATH + "/libweb_engine.so";
109     webEngineHandle = dlopen(engineLibDir.c_str(), RTLD_NOW | RTLD_GLOBAL);
110 
111     // load libnweb_render
112     const std::string renderLibDir = NWEB_HAP_LIB_PATH + "/libnweb_render.so";
113     g_nwebRenderHandle = dlopen(renderLibDir.c_str(), RTLD_NOW | RTLD_GLOBAL);
114 #endif
115     if (webEngineHandle == nullptr) {
116         APPSPAWN_LOGE("Fail to dlopen libweb_engine.so, [%{public}s]", dlerror());
117     } else {
118         APPSPAWN_LOGI("Success to dlopen libweb_engine.so");
119     }
120 
121     if (g_nwebRenderHandle == nullptr) {
122         APPSPAWN_LOGE("Fail to dlopen libnweb_render.so, [%{public}s]", dlerror());
123         return;
124     } else {
125         APPSPAWN_LOGI("Success to dlopen libnweb_render.so");
126     }
127 }
128 
LoadExtendLibNweb(AppSpawnContent * content)129 void LoadExtendLibNweb(AppSpawnContent *content)
130 {
131     APPSPAWN_LOGI("LoadExtendLibNweb");
132 #if defined(webview_arm)
133     PreLoadWebEngineLibs();
134 #endif
135 }
136 
137 #ifdef WITH_SECCOMP
SetSeccompPolicyForRenderer(void * nwebRenderHandle)138 static bool SetSeccompPolicyForRenderer(void *nwebRenderHandle)
139 {
140     if (IsEnableSeccomp()) {
141         using SeccompFuncType = bool (*)(void);
142         SeccompFuncType funcSetRendererSeccompPolicy =
143                 reinterpret_cast<SeccompFuncType>(dlsym(nwebRenderHandle, "SetRendererSeccompPolicy"));
144         if (funcSetRendererSeccompPolicy == nullptr) {
145             APPSPAWN_LOGE("SetRendererSeccompPolicy dlsym ERROR=%{public}s", dlerror());
146             return false;
147         }
148         if (!funcSetRendererSeccompPolicy()) {
149             APPSPAWN_LOGE("Failed to set seccomp policy.");
150             return false;
151         }
152     }
153     return true;
154 }
155 #endif
156 
RunChildProcessorNweb(AppSpawnContent * content,AppSpawnClient * client)157 void RunChildProcessorNweb(AppSpawnContent *content, AppSpawnClient *client)
158 {
159     APPSPAWN_LOGI("RunChildProcessorNweb");
160 
161 #if defined(webview_arm64) || defined(webview_x86_64)
162     PreLoadWebEngineLibs();
163 #endif
164 
165 #ifdef WITH_SECCOMP
166     if (!SetSeccompPolicyForRenderer(g_nwebRenderHandle)) {
167         return;
168     }
169 #endif
170 
171     AppSpawnClientExt *appProperty = reinterpret_cast<AppSpawnClientExt *>(client);
172     using FuncType = void (*)(const char *cmd);
173 
174     FuncType funcNWebRenderMain = reinterpret_cast<FuncType>(dlsym(g_nwebRenderHandle, "NWebRenderMain"));
175     if (funcNWebRenderMain == nullptr) {
176         APPSPAWN_LOGI("webviewspawn dlsym ERROR=%{public}s", dlerror());
177         return;
178     }
179 
180     funcNWebRenderMain(appProperty->property.renderCmd);
181 }
182 
DumpRenderProcessExitedMap()183 static void DumpRenderProcessExitedMap()
184 {
185     APPSPAWN_LOGI("dump render process exited array:");
186 
187     for (auto& it : g_renderProcessMap) {
188         APPSPAWN_LOGV("[pid, time, exitedStatus] = [%{public}d, %{public}ld, %{public}d]",
189             it.first, static_cast<long>(it.second.recordTime_), it.second.exitStatus_);
190     }
191 }
192 
RecordRenderProcessExitedStatus(pid_t pid,int status)193 void RecordRenderProcessExitedStatus(pid_t pid, int status)
194 {
195     std::lock_guard<std::mutex> lock(g_mutex);
196     if (g_renderProcessMap.size() < RENDER_PROCESS_MAX_NUM) {
197         RenderProcessNode node(time(nullptr), status);
198         g_renderProcessMap.insert({pid, node});
199         return;
200     }
201 
202     APPSPAWN_LOGV("render process map size reach max, need to erase oldest data.");
203     DumpRenderProcessExitedMap();
204     auto oldestData = std::min_element(g_renderProcessMap.begin(), g_renderProcessMap.end(),
205         [](const std::pair<int32_t, RenderProcessNode>& left, const std::pair<int32_t, RenderProcessNode>& right) {
206             return left.second.recordTime_ < right.second.recordTime_;
207         });
208 
209     g_renderProcessMap.erase(oldestData);
210     RenderProcessNode node(time(nullptr), status);
211     g_renderProcessMap.insert({pid, node});
212     DumpRenderProcessExitedMap();
213 }
214 
GetRenderProcessTerminationStatus(int32_t pid,int * status)215 static int GetRenderProcessTerminationStatus(int32_t pid, int *status)
216 {
217     if (status == nullptr) {
218         return -1;
219     }
220 
221     std::lock_guard<std::mutex> lock(g_mutex);
222     auto it = g_renderProcessMap.find(pid);
223     if (it != g_renderProcessMap.end()) {
224         *status = it->second.exitStatus_;
225         g_renderProcessMap.erase(it);
226         return 0;
227     }
228 
229     APPSPAWN_LOGE("not find pid[%{public}d] in render process exited map", pid);
230     DumpRenderProcessExitedMap();
231     return -1;
232 }
233 
GetProcessTerminationStatusInner(int32_t pid,int * status)234 static int GetProcessTerminationStatusInner(int32_t pid, int *status)
235 {
236     if (status == nullptr) {
237         return -1;
238     }
239 
240     if (GetRenderProcessTerminationStatus(pid, status) == 0) {
241         // this shows that the parent process has received SIGCHLD signal.
242         return 0;
243     }
244 
245     if (kill(pid, SIGKILL) != 0) {
246         APPSPAWN_LOGE("unable to kill render process, pid: %{public}d ret %{public}d", pid, errno);
247     }
248 
249     pid_t exitPid = waitpid(pid, status, WNOHANG);
250     if (exitPid != pid) {
251         APPSPAWN_LOGE("waitpid failed, return : %{public}d, pid: %{public}d, status: %{public}d",
252             exitPid, pid, *status);
253         return -1;
254     }
255     return 0;
256 }
257 
GetProcessTerminationStatus(AppSpawnClient * client)258 int GetProcessTerminationStatus(AppSpawnClient *client)
259 {
260     AppSpawnClientExt *appProperty = reinterpret_cast<AppSpawnClientExt *>(client);
261     APPSPAWN_CHECK(appProperty != nullptr, return -1, "Invalid client");
262     int exitStatus = 0;
263     int ret = GetProcessTerminationStatusInner(appProperty->property.pid, &exitStatus);
264     if (ret) {
265         exitStatus = ret;
266     }
267     APPSPAWN_LOGI("AppSpawnServer::get render process termination status, status ="
268         "%{public}d pid = %{public}d uid %{public}d %{public}s %{public}s",
269         exitStatus, appProperty->property.pid, appProperty->property.uid,
270         appProperty->property.processName, appProperty->property.bundleName);
271     return exitStatus;
272 }
273