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