1 /*
2  * Copyright (C) 2024 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 "IpcProxy.h"
17 #include <cstdlib>
18 #include <cstring>
19 #include <hilog/log.h>
20 #include <fcntl.h>
21 #include <future>
22 #include <unistd.h>
23 #include "napi/native_api.h"
24 #include "AbilityKit/native_child_process.h"
25 #include "ChildProcess.h"
26 #include "loghelper.h"
27 
28 #undef LOG_DOMAIN
29 #undef LOG_TAG
30 #define LOG_DOMAIN 0x3200
31 #define LOG_TAG "CHILD_TAG"
32 
33 static ChildProcess g_childProcess;
34 static IpcProxy *g_ipcProxyPnt = nullptr;
35 static std::promise<int> *g_promiseStartProcess = nullptr;
36 
37 extern "C" {
38 
NativeChildProcess_OnConnect()39 OHIPCRemoteStub* NativeChildProcess_OnConnect()
40 {
41     OH_LOG_INFO(LOG_APP, "Child process - OnConnect");
42     return g_childProcess.GetIpcStub();
43 }
44 
NativeChildProcess_MainProc()45 void NativeChildProcess_MainProc()
46 {
47     OH_LOG_INFO(LOG_APP, "Child process - MainProc started");
48     g_childProcess.MainProc();
49     OH_LOG_INFO(LOG_APP, "Child process - MainProc end");
50 }
51 
52 } // extern "C"
53 
OnNativeChildProcessStarted(int errCode, OHIPCRemoteProxy *remoteProxy)54 static void OnNativeChildProcessStarted(int errCode, OHIPCRemoteProxy *remoteProxy)
55 {
56     OH_LOG_INFO(LOG_APP, "Main process - OnNativeChildProcessStarted %{public}d", errCode);
57     g_ipcProxyPnt = new (std::nothrow) IpcProxy(remoteProxy);
58     if (g_ipcProxyPnt == nullptr) {
59         OH_LOG_ERROR(LOG_APP, "Main process - Alloc ipc proxy object failed!");
60         OH_IPCRemoteProxy_Destroy(remoteProxy);
61     }
62 
63     if (g_promiseStartProcess != nullptr) {
64         g_promiseStartProcess->set_value(errCode);
65     }
66 }
67 
ChildProcessAdd(napi_env env, napi_callback_info info)68 static napi_value ChildProcessAdd(napi_env env, napi_callback_info info)
69 {
70     int32_t result = INT32_MIN;
71     if (g_ipcProxyPnt != nullptr) {
72         size_t argc = 2;
73         napi_value args[2] = { nullptr };
74         napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
75         int32_t value0;
76         napi_get_value_int32(env, args[0], &value0);
77         int32_t value1;
78         napi_get_value_int32(env, args[1], &value1);
79 
80         result = g_ipcProxyPnt->Add(value0, value1);
81         OH_LOG_INFO(LOG_APP, "Main process - ChildProcessAdd %{public}d+%{public}d=%{public}d",
82             value0, value1, result);
83     } else {
84         OH_LOG_ERROR(LOG_APP, "Main process - Child process not started");
85     }
86 
87     napi_value sumNapi;
88     napi_create_int32(env, result, &sumNapi);
89     return sumNapi;
90 }
91 
StartNativeChildProcess(napi_env env, napi_callback_info info)92 static napi_value StartNativeChildProcess(napi_env env, napi_callback_info info)
93 {
94     std::promise<int> promise;
95     g_promiseStartProcess = &promise;
96 
97     size_t argc = 1;
98     napi_value args[1] = { nullptr };
99     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
100 
101     char libName[64];
102     size_t nameLen;
103     napi_get_value_string_utf8(env, args[0], libName, sizeof(libName), &nameLen);
104 
105     int32_t ret = OH_Ability_CreateNativeChildProcess(libName, OnNativeChildProcessStarted);
106     OH_LOG_INFO(LOG_APP, "Main process - StartNativeChildProcess Lib:%{public}s ret:%{public}d", libName, ret);
107 
108     if (ret == NCP_NO_ERROR) {
109         auto future = promise.get_future();
110         OH_LOG_INFO(LOG_APP, "Main process - Wait for call back");
111         ret = future.get();
112     }
113 
114     g_promiseStartProcess = nullptr;
115     napi_value napiRet;
116     napi_create_int32(env, ret, &napiRet);
117     return napiRet;
118 }
119 
RequestExitChildProcess(napi_env env, napi_callback_info info)120 static napi_value RequestExitChildProcess(napi_env env, napi_callback_info info)
121 {
122     int32_t ret = 0;
123     if (g_ipcProxyPnt != nullptr && g_ipcProxyPnt->RequestExitChildProcess()) {
124         ret = 1;
125         delete g_ipcProxyPnt;
126         g_ipcProxyPnt = nullptr;
127         OH_LOG_INFO(LOG_APP, "Main process - RequestExitChildProcess successed");
128     }
129 
130     napi_value napiRet;
131     napi_create_int32(env, ret, &napiRet);
132     return napiRet;
133 }
134 
CallApiWithNullCallback(napi_env env, napi_callback_info info)135 static napi_value CallApiWithNullCallback(napi_env env, napi_callback_info info)
136 {
137     int32_t ret = OH_Ability_CreateNativeChildProcess("libentry.so", nullptr);
138     napi_value napiRet;
139     napi_create_int32(env, ret, &napiRet);
140     return napiRet;
141 }
142 
CallApiWithNullLibName(napi_env env, napi_callback_info info)143 static napi_value CallApiWithNullLibName(napi_env env, napi_callback_info info)
144 {
145     int32_t ret = OH_Ability_CreateNativeChildProcess(nullptr, OnNativeChildProcessStarted);
146     napi_value napiRet;
147     napi_create_int32(env, ret, &napiRet);
148     return napiRet;
149 }
150 
CallApiWithNull(napi_env env, napi_callback_info info)151 static napi_value CallApiWithNull(napi_env env, napi_callback_info info)
152 {
153     int32_t ret = OH_Ability_CreateNativeChildProcess(nullptr, nullptr);
154     napi_value napiRet;
155     napi_create_int32(env, ret, &napiRet);
156     return napiRet;
157 }
158 
ChildProcessStartNewProcess(napi_env env, napi_callback_info info)159 static napi_value ChildProcessStartNewProcess(napi_env env, napi_callback_info info)
160 {
161     int32_t ret = INT32_MIN;
162     if (g_ipcProxyPnt != nullptr) {
163         ret = g_ipcProxyPnt->StartNativeChildProcess();
164         OH_LOG_INFO(LOG_APP, "Main process - StartNativeChildProcess ret:%{public}d", ret);
165     }
166 
167     napi_value napiRet;
168     napi_create_int32(env, ret, &napiRet);
169     return napiRet;
170 }
171 
BusyTest(napi_env env, napi_callback_info info)172 static napi_value BusyTest(napi_env env, napi_callback_info info)
173 {
174     napi_value napiRet;
175     std::promise<int> promise;
176     g_promiseStartProcess = &promise;
177     int32_t ret = OH_Ability_CreateNativeChildProcess("libbusytest.so", OnNativeChildProcessStarted);
178     if (ret != NCP_NO_ERROR) {
179         OH_LOG_INFO(LOG_APP, "Main process - StartNativeChildProcess for busy test failed! ret:%{public}d", ret);
180         napi_create_int32(env, ret, &napiRet);
181         return napiRet;
182     }
183 
184     ret = OH_Ability_CreateNativeChildProcess("libentry.so", OnNativeChildProcessStarted);
185 
186     auto future = promise.get_future();
187     OH_LOG_INFO(LOG_APP, "Main process - Wait for busy test call back");
188     future.wait();
189 
190     napi_create_int32(env, ret, &napiRet);
191     return napiRet;
192 }
193 
StartChildWithArgs(NativeChildProcess_IsolationMode mode)194 static Ability_NativeChildProcess_ErrCode StartChildWithArgs(NativeChildProcess_IsolationMode mode)
195 {
196     int32_t pid = -1;
197     NativeChildProcess_Args args;
198     auto testParam = "testEntryParams";
199     args.entryParams = (char*)malloc(sizeof(char) * strlen(testParam) + 1);
200     (void)strcpy(args.entryParams, testParam);
201 
202     auto fd1Name = "fd1";
203     args.fdList.head = (NativeChildProcess_Fd*)malloc(sizeof(NativeChildProcess_Fd));
204     args.fdList.head->fdName = (char*)malloc(sizeof(char) * strlen(fd1Name) + 1);
205     (void)strcpy(args.fdList.head->fdName, fd1Name);
206 
207     auto path = "data/storage/el2/base/files/test.txt";
208     int32_t fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
209     auto testString = "test";
210     write(fd, testString, strlen(testString));
211     close(fd);
212     fd = open(path, O_RDWR | O_TRUNC, 0644);
213     args.fdList.head->fd = fd;
214     args.fdList.head->next = NULL;
215 
216     NativeChildProcess_Options options = {
217         .isolationMode = mode
218     };
219     OH_LOG_INFO(LOG_APP, "===================Ability_NativeChildProcess before");
220     Ability_NativeChildProcess_ErrCode ret = OH_Ability_StartNativeChildProcess(
221         "libentry.so:Main", args, options, &pid);
222     OH_LOG_INFO(LOG_APP, "===================Ability_NativeChildProcess_ErrCode: %{public}d", ret);
223     close(fd);
224     return ret;
225 }
226 
StartChildWithNoArgs()227 static Ability_NativeChildProcess_ErrCode StartChildWithNoArgs()
228 {
229     int32_t pid = -1;
230     NativeChildProcess_Args args;
231     args.entryParams = NULL;
232     args.fdList.head = NULL;
233 
234     NativeChildProcess_Options options = {
235         .isolationMode = NCP_ISOLATION_MODE_ISOLATED
236     };
237     OH_LOG_INFO(LOG_APP, "===================Ability_NativeChildProcess before");
238     Ability_NativeChildProcess_ErrCode ret = OH_Ability_StartNativeChildProcess(
239         "libentry.so:Main", args, options, &pid);
240     OH_LOG_INFO(LOG_APP, "===================Ability_NativeChildProcess_ErrCode: %{public}d", ret);
241     return ret;
242 }
243 
StartChildIsolated(napi_env env, napi_callback_info info)244 static napi_value StartChildIsolated(napi_env env, napi_callback_info info)
245 {
246     OH_LOG_INFO(LOG_APP, "===================StartChildIsolated");
247     int32_t ret = static_cast<int32_t>(StartChildWithArgs(NCP_ISOLATION_MODE_ISOLATED));
248     napi_value napiRet;
249     napi_create_int32(env, ret, &napiRet);
250     return napiRet;
251 }
252 
StartChildNormal(napi_env env, napi_callback_info info)253 static napi_value StartChildNormal(napi_env env, napi_callback_info info)
254 {
255     OH_LOG_INFO(LOG_APP, "===================StartChildNormal");
256     int32_t ret = static_cast<int32_t>(StartChildWithArgs(NCP_ISOLATION_MODE_NORMAL));
257     napi_value napiRet;
258     napi_create_int32(env, ret, &napiRet);
259     return napiRet;
260 }
261 
262 
StartChildNoArgs(napi_env env, napi_callback_info info)263 static napi_value StartChildNoArgs(napi_env env, napi_callback_info info)
264 {
265     OH_LOG_INFO(LOG_APP, "===================StartChildWithNoArgs");
266     int32_t ret = static_cast<int32_t>(StartChildWithNoArgs());
267     napi_value napiRet;
268     napi_create_int32(env, ret, &napiRet);
269     return napiRet;
270 }
271 
272 EXTERN_C_START
Init(napi_env env, napi_value exports)273 static napi_value Init(napi_env env, napi_value exports)
274 {
275     napi_property_descriptor desc[] = {
276         { "childProcessAdd", nullptr, ChildProcessAdd, nullptr, nullptr, nullptr, napi_default, nullptr },
277         { "startNativeChildProcess", nullptr, StartNativeChildProcess,
278             nullptr, nullptr, nullptr, napi_default, nullptr },
279         { "requestExitChildProcess", nullptr, RequestExitChildProcess,
280             nullptr, nullptr, nullptr, napi_default, nullptr },
281         { "callApiWithNullCallback", nullptr, CallApiWithNullCallback,
282             nullptr, nullptr, nullptr, napi_default, nullptr },
283         { "callApiWithNullLibName", nullptr, CallApiWithNullLibName,
284             nullptr, nullptr, nullptr, napi_default, nullptr },
285         { "callApiWithNull", nullptr, CallApiWithNull,
286             nullptr, nullptr, nullptr, napi_default, nullptr },
287         { "childProcessStartNewProcess", nullptr, ChildProcessStartNewProcess,
288             nullptr, nullptr, nullptr, napi_default, nullptr },
289         { "busyTest", nullptr, BusyTest,
290             nullptr, nullptr, nullptr, napi_default, nullptr },
291         { "startChildIsolated", nullptr, StartChildIsolated,
292             nullptr, nullptr, nullptr, napi_default, nullptr },
293         { "startChildNormal", nullptr, StartChildNormal,
294             nullptr, nullptr, nullptr, napi_default, nullptr },
295         { "startChildNoArgs", nullptr, StartChildNoArgs,
296             nullptr, nullptr, nullptr, napi_default, nullptr }
297     };
298     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
299     return exports;
300 }
301 EXTERN_C_END
302 
303 static napi_module demoModule = {
304     .nm_version = 1,
305     .nm_flags = 0,
306     .nm_filename = nullptr,
307     .nm_register_func = Init,
308     .nm_modname = "entry",
309     .nm_priv = ((void*)0),
310     .reserved = { 0 },
311 };
312 
RegisterEntryModule(void)313 extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
314 {
315     napi_module_register(&demoModule);
316 }
317