• 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     std::map<int32_t, RenderProcessNode> g_renderProcessMap;
50     std::mutex g_mutex;
51 
52 #if defined(webview_arm64)
53     const std::string NWEB_HAP_LIB_PATH = "/data/storage/el1/bundle/nweb/libs/arm64";
54 #elif defined(webview_x86_64)
55     const std::string NWEB_HAP_LIB_PATH = "/data/storage/el1/bundle/nweb/libs/x86_64";
56 #else
57     const std::string NWEB_HAP_LIB_PATH = "/data/storage/el1/bundle/nweb/libs/arm";
58 #endif
59 }
60 
LoadExtendLibNweb(AppSpawnContent * content)61 void LoadExtendLibNweb(AppSpawnContent *content)
62 {
63 }
64 
65 #ifdef WITH_SECCOMP
SetSeccompPolicyForRenderer(void * nwebRenderHandle)66 static bool SetSeccompPolicyForRenderer(void *nwebRenderHandle)
67 {
68     if (IsEnableSeccomp()) {
69         using SeccompFuncType = bool (*)(void);
70         SeccompFuncType funcSetRendererSeccompPolicy =
71                 reinterpret_cast<SeccompFuncType>(dlsym(nwebRenderHandle, "SetRendererSeccompPolicy"));
72         if (funcSetRendererSeccompPolicy == nullptr) {
73             APPSPAWN_LOGE("SetRendererSeccompPolicy dlsym ERROR=%{public}s", dlerror());
74             return false;
75         }
76         if (!funcSetRendererSeccompPolicy()) {
77             APPSPAWN_LOGE("Failed to set seccomp policy.");
78             return false;
79         }
80     }
81     return true;
82 }
83 #endif
84 
RunChildProcessorNweb(AppSpawnContent * content,AppSpawnClient * client)85 void RunChildProcessorNweb(AppSpawnContent *content, AppSpawnClient *client)
86 {
87     APPSPAWN_LOGI("RunChildProcessorNweb");
88     void *webEngineHandle = nullptr;
89     void *nwebRenderHandle = nullptr;
90 
91 #ifdef __MUSL__
92     Dl_namespace dlns;
93     dlns_init(&dlns, "nweb_ns");
94     dlns_create(&dlns, NWEB_HAP_LIB_PATH.c_str());
95 
96     // preload libweb_engine
97     webEngineHandle = dlopen_ns(&dlns, "libweb_engine.so", RTLD_NOW | RTLD_GLOBAL);
98 
99     // load libnweb_render
100     nwebRenderHandle = dlopen_ns(&dlns, "libnweb_render.so", RTLD_NOW | RTLD_GLOBAL);
101 #else
102     // preload libweb_engine
103     const std::string engineLibDir = NWEB_HAP_LIB_PATH + "/libweb_engine.so";
104     webEngineHandle = dlopen(engineLibDir.c_str(), RTLD_NOW | RTLD_GLOBAL);
105 
106     // load libnweb_render
107     const std::string renderLibDir = NWEB_HAP_LIB_PATH + "/libnweb_render.so";
108     nwebRenderHandle = dlopen(renderLibDir.c_str(), RTLD_NOW | RTLD_GLOBAL);
109 #endif
110     if (webEngineHandle == nullptr) {
111         APPSPAWN_LOGE("Fail to dlopen libweb_engine.so, [%{public}s]", dlerror());
112     } else {
113         APPSPAWN_LOGI("Success to dlopen libweb_engine.so");
114     }
115 
116     if (nwebRenderHandle == nullptr) {
117         APPSPAWN_LOGE("Fail to dlopen libnweb_render.so, [%{public}s]", dlerror());
118         return;
119     } else {
120         APPSPAWN_LOGI("Success to dlopen libnweb_render.so");
121     }
122 
123 #ifdef WITH_SECCOMP
124     if (!SetSeccompPolicyForRenderer(nwebRenderHandle)) {
125         return;
126     }
127 #endif
128 
129     AppSpawnClientExt *appProperty = reinterpret_cast<AppSpawnClientExt *>(client);
130     using FuncType = void (*)(const char *cmd);
131 
132     FuncType funcNWebRenderMain = reinterpret_cast<FuncType>(dlsym(nwebRenderHandle, "NWebRenderMain"));
133     if (funcNWebRenderMain == nullptr) {
134         APPSPAWN_LOGI("webviewspawn dlsym ERROR=%{public}s", dlerror());
135         return;
136     }
137 
138     funcNWebRenderMain(appProperty->property.renderCmd);
139 }
140 
DumpRenderProcessExitedMap()141 static void DumpRenderProcessExitedMap()
142 {
143     APPSPAWN_LOGI("dump render process exited array:");
144 
145     for (auto& it : g_renderProcessMap) {
146         APPSPAWN_LOGV("[pid, time, exitedStatus] = [%{public}d, %{public}ld, %{public}d]",
147             it.first, static_cast<long>(it.second.recordTime_), it.second.exitStatus_);
148     }
149 }
150 
RecordRenderProcessExitedStatus(pid_t pid,int status)151 void RecordRenderProcessExitedStatus(pid_t pid, int status)
152 {
153     std::lock_guard<std::mutex> lock(g_mutex);
154     if (g_renderProcessMap.size() < RENDER_PROCESS_MAX_NUM) {
155         RenderProcessNode node(time(nullptr), status);
156         g_renderProcessMap.insert({pid, node});
157         return;
158     }
159 
160     APPSPAWN_LOGV("render process map size reach max, need to erase oldest data.");
161     DumpRenderProcessExitedMap();
162     auto oldestData = std::min_element(g_renderProcessMap.begin(), g_renderProcessMap.end(),
163         [](const std::pair<int32_t, RenderProcessNode>& left, const std::pair<int32_t, RenderProcessNode>& right) {
164             return left.second.recordTime_ < right.second.recordTime_;
165         });
166 
167     g_renderProcessMap.erase(oldestData);
168     RenderProcessNode node(time(nullptr), status);
169     g_renderProcessMap.insert({pid, node});
170     DumpRenderProcessExitedMap();
171 }
172 
GetRenderProcessTerminationStatus(int32_t pid,int * status)173 static int GetRenderProcessTerminationStatus(int32_t pid, int *status)
174 {
175     if (status == nullptr) {
176         return -1;
177     }
178 
179     std::lock_guard<std::mutex> lock(g_mutex);
180     auto it = g_renderProcessMap.find(pid);
181     if (it != g_renderProcessMap.end()) {
182         *status = it->second.exitStatus_;
183         g_renderProcessMap.erase(it);
184         return 0;
185     }
186 
187     APPSPAWN_LOGE("not find pid[%{public}d] in render process exited map", pid);
188     DumpRenderProcessExitedMap();
189     return -1;
190 }
191 
GetProcessTerminationStatusInner(int32_t pid,int * status)192 static int GetProcessTerminationStatusInner(int32_t pid, int *status)
193 {
194     if (status == nullptr) {
195         return -1;
196     }
197 
198     if (GetRenderProcessTerminationStatus(pid, status) == 0) {
199         // this shows that the parent process has received SIGCHLD signal.
200         return 0;
201     }
202     if (pid <= 0) {
203         return -1;
204     }
205     if (kill(pid, SIGKILL) != 0) {
206         APPSPAWN_LOGE("unable to kill render process, pid: %{public}d ret %{public}d", pid, errno);
207     }
208 
209     pid_t exitPid = waitpid(pid, status, WNOHANG);
210     if (exitPid != pid) {
211         APPSPAWN_LOGE("waitpid failed, return : %{public}d, pid: %{public}d, status: %{public}d",
212             exitPid, pid, *status);
213         return -1;
214     }
215     return 0;
216 }
217 
GetProcessTerminationStatus(AppSpawnClient * client)218 int GetProcessTerminationStatus(AppSpawnClient *client)
219 {
220     AppSpawnClientExt *appProperty = reinterpret_cast<AppSpawnClientExt *>(client);
221     APPSPAWN_CHECK(appProperty != nullptr, return -1, "Invalid client");
222     int exitStatus = 0;
223     int ret = GetProcessTerminationStatusInner(appProperty->property.pid, &exitStatus);
224     if (ret) {
225         exitStatus = ret;
226     }
227     APPSPAWN_LOGI("AppSpawnServer::get render process termination status, status ="
228         "%{public}d pid = %{public}d uid %{public}d %{public}s %{public}s",
229         exitStatus, appProperty->property.pid, appProperty->property.uid,
230         appProperty->property.processName, appProperty->property.bundleName);
231     return exitStatus;
232 }
233