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 "appspawn_test_cmder.h"
17
18 #include <cerrno>
19 #include <cstdint>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <fcntl.h>
23 #include <string>
24 #include <termios.h>
25 #include <unistd.h>
26
27 #include <sys/stat.h>
28
29 #include "appspawn.h"
30 #include "appspawn_msg.h"
31 #include "appspawn_utils.h"
32 #include "cJSON.h"
33 #include "command_lexer.h"
34 #include "json_utils.h"
35 #include "securec.h"
36 #include "thread_manager.h"
37
38 #define MAX_THREAD 10
39 #define MAX_SEND 200
40 #define PTY_PATH_SIZE 128
41
42 namespace OHOS {
43 namespace AppSpawnModuleTest {
44 static const std::string g_defaultAppInfo = "{ \
45 \"msg-type\": \"MSG_APP_SPAWN\", \
46 \"msg-flags\": [1, 2 ], \
47 \"process-name\" : \"com.example.myapplication\", \
48 \"dac-info\" : { \
49 \"uid\" : 20010043, \
50 \"gid\" : 20010043,\
51 \"gid-table\" : [],\
52 \"user-name\" : \"\" \
53 },\
54 \"access-token\" : {\
55 \"accessTokenIdEx\" : 537854093\
56 },\
57 \"permission\" : [\
58 \"ohos.permission.MANAGE_PRIVATE_PHOTOS\",\
59 \"ohos.permission.ACTIVATE_THEME_PACKAGE\"\
60 ],\
61 \"internet-permission\" : {\
62 \"set-allow-internet\" : 0,\
63 \"allow-internet\" : 0\
64 },\
65 \"bundle-info\" : {\
66 \"bundle-index\" : 0,\
67 \"bundle-name\" : \"com.example.myapplication\" \
68 },\
69 \"owner-id\" : \"\",\
70 \"render-cmd\" : \"1234567890\",\
71 \"domain-info\" : {\
72 \"hap-flags\" : 0,\
73 \"apl\" : \"system_core\"\
74 },\
75 \"ext-info\" : [\
76 {\
77 \"name\" : \"test\",\
78 \"value\" : \"4444444444444444444\" \
79 } \
80 ]\
81 }";
82
83 static const char *APPSPAWN_TEST_USAGE = "usage: AppSpawnTest <options> \n"
84 "options list:\n"
85 " --help list available commands\n"
86 " --file xx file path with app info\n"
87 " --thread xx use multi-thread to send message\n"
88 " --type xx send msg type \n"
89 " --pid xx render terminate pid\n"
90 " --mode nwebspawn send message to nwebspawn service\n"
91 " --mode nativespawn send message to nativespawn service\n";
92
ProcessArgs(int argc,char * const argv[])93 int AppSpawnTestCommander::ProcessArgs(int argc, char *const argv[])
94 {
95 int sendMsg = 0;
96 msgType_ = MAX_TYPE_INVALID;
97 for (int32_t i = 0; i < argc; i++) {
98 if (argv[i] == nullptr) {
99 continue;
100 }
101 if (strcmp(argv[i], "--file") == 0 && ((i + 1) < argc)) { // test file
102 i++;
103 testFileName_ = argv[i];
104 sendMsg = 1;
105 } else if (strcmp(argv[i], "--thread") == 0 && ((i + 1) < argc)) { // use thread
106 i++;
107 threadCount_ = atoi(argv[i]);
108 if (threadCount_ > MAX_THREAD) {
109 threadCount_ = MAX_THREAD;
110 }
111 sendMsg = 1;
112 } else if (strcmp(argv[i], "--mode") == 0 && ((i + 1) < argc)) {
113 i++;
114 if (strcmp(argv[i], "nwebspawn") == 0) {
115 appSpawn_ = 0;
116 } else if (strcmp(argv[i], "nativespawn") == 0) {
117 appSpawn_ = 2; // 2 is nwebspawn
118 } else {
119 appSpawn_ = 1;
120 }
121 sendMsg = 1;
122 } else if (strcmp(argv[i], "--type") == 0 && ((i + 1) < argc)) {
123 i++;
124 msgType_ = atoi(argv[i]);
125 sendMsg = 1;
126 } else if (strcmp(argv[i], "--pid") == 0 && ((i + 1) < argc)) {
127 i++;
128 msgType_ = MSG_GET_RENDER_TERMINATION_STATUS;
129 terminatePid_ = atoi(argv[i]);
130 sendMsg = 1;
131 } else if (strcmp(argv[i], "--help") == 0) {
132 printf("%s\n", APPSPAWN_TEST_USAGE);
133 return 1;
134 } else if (strcmp(argv[i], "--send") == 0 || strcmp(argv[i], "send") == 0) {
135 sendMsg = 1;
136 }
137 }
138 if (sendMsg == 0) {
139 printf("%s\n", APPSPAWN_TEST_USAGE);
140 return 1;
141 }
142 return 0;
143 }
144
GetUint32ArrayFromJson(const cJSON * json,const char * name,uint32_t dataArray[],uint32_t maxCount)145 uint32_t AppSpawnTestCommander::GetUint32ArrayFromJson(const cJSON *json,
146 const char *name, uint32_t dataArray[], uint32_t maxCount)
147 {
148 APPSPAWN_CHECK(json != NULL, return 0, "Invalid json");
149 APPSPAWN_CHECK(name != NULL, return 0, "Invalid name");
150 APPSPAWN_CHECK(dataArray != NULL, return 0, "Invalid dataArray");
151 APPSPAWN_CHECK(cJSON_IsObject(json), return 0, "json is not object.");
152 cJSON *array = cJSON_GetObjectItemCaseSensitive(json, name);
153 APPSPAWN_CHECK_ONLY_EXPER(array != NULL, return 0);
154 APPSPAWN_CHECK(cJSON_IsArray(array), return 0, "json is not object.");
155
156 uint32_t count = 0;
157 uint32_t arrayLen = cJSON_GetArraySize(array);
158 for (int i = 0; i < arrayLen; i++) {
159 cJSON *item = cJSON_GetArrayItem(array, i);
160 uint32_t value = (uint32_t)cJSON_GetNumberValue(item);
161 if (count < maxCount) {
162 dataArray[count++] = value;
163 }
164 }
165 return count;
166 }
167
AddBundleInfoFromJson(const cJSON * appInfoConfig,AppSpawnReqMsgHandle reqHandle)168 int AppSpawnTestCommander::AddBundleInfoFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
169 {
170 cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "bundle-info");
171 APPSPAWN_CHECK_ONLY_EXPER(config, return 0);
172
173 uint32_t bundleIndex = GetIntValueFromJsonObj(config, "bundle-index", 0);
174 char *bundleName = GetStringFromJsonObj(config, "bundle-name");
175 int ret = AppSpawnReqMsgSetBundleInfo(reqHandle, bundleIndex, bundleName);
176 APPSPAWN_CHECK(ret == 0, return ret, "Failed to add bundle info req %{public}s", bundleName);
177 return 0;
178 }
179
AddDacInfoFromJson(const cJSON * appInfoConfig,AppSpawnReqMsgHandle reqHandle)180 int AppSpawnTestCommander::AddDacInfoFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
181 {
182 cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "dac-info");
183 APPSPAWN_CHECK_ONLY_EXPER(config, return 0);
184
185 AppDacInfo info = {};
186 info.uid = GetIntValueFromJsonObj(config, "uid", 0);
187 info.gid = GetIntValueFromJsonObj(config, "gid", 0);
188 info.gidCount = GetUint32ArrayFromJson(config, "gid-table", info.gidTable, APP_MAX_GIDS);
189 char *userName = GetStringFromJsonObj(config, "user-name");
190 if (userName != nullptr) {
191 int ret = strcpy_s(info.userName, sizeof(info.userName), userName);
192 APPSPAWN_CHECK(ret == 0, return ret, "Failed to add userName info req %{public}s", userName);
193 }
194 return AppSpawnReqMsgSetAppDacInfo(reqHandle, &info);
195 }
196
AddInternetPermissionInfoFromJson(const cJSON * appInfoConfig,AppSpawnReqMsgHandle reqHandle)197 int AppSpawnTestCommander::AddInternetPermissionInfoFromJson(
198 const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
199 {
200 cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "internet-permission");
201 APPSPAWN_CHECK_ONLY_EXPER(config, return 0);
202
203 uint8_t setAllowInternet = GetIntValueFromJsonObj(config, "set-allow-internet", 0);
204 uint8_t allowInternet = GetIntValueFromJsonObj(config, "allow-internet", 0);
205 return AppSpawnReqMsgSetAppInternetPermissionInfo(reqHandle, allowInternet, setAllowInternet);
206 }
207
AddAccessTokenFromJson(const cJSON * appInfoConfig,AppSpawnReqMsgHandle reqHandle)208 int AppSpawnTestCommander::AddAccessTokenFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
209 {
210 cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "access-token");
211 APPSPAWN_CHECK_ONLY_EXPER(config, return 0);
212
213 uint64_t accessTokenIdEx = GetIntValueFromJsonObj(config, "accessTokenIdEx", 0);
214 return AppSpawnReqMsgSetAppAccessToken(reqHandle, accessTokenIdEx);
215 }
216
AddDomainInfoFromJson(const cJSON * appInfoConfig,AppSpawnReqMsgHandle reqHandle)217 int AppSpawnTestCommander::AddDomainInfoFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
218 {
219 cJSON *config = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "domain-info");
220 APPSPAWN_CHECK_ONLY_EXPER(config, return 0);
221
222 uint32_t hapFlags = GetIntValueFromJsonObj(config, "hap-flags", 0);
223 char *apl = GetStringFromJsonObj(config, "apl");
224 int ret = AppSpawnReqMsgSetAppDomainInfo(reqHandle, hapFlags, apl);
225 APPSPAWN_CHECK(ret == 0, return ret, "Failed to domain info");
226 return 0;
227 }
228
AddExtTlv(const cJSON * appInfoConfig,AppSpawnReqMsgHandle reqHandle)229 int AppSpawnTestCommander::AddExtTlv(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
230 {
231 cJSON *configs = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "ext-info");
232 APPSPAWN_CHECK_ONLY_EXPER(configs != nullptr, return 0);
233
234 int ret = 0;
235 uint32_t count = cJSON_GetArraySize(configs);
236 for (unsigned int j = 0; j < count; j++) {
237 cJSON *config = cJSON_GetArrayItem(configs, j);
238
239 char *name = GetStringFromJsonObj(config, "name");
240 char *value = GetStringFromJsonObj(config, "value");
241 APPSPAWN_LOGV("ext-info %{public}s %{public}s", name, value);
242 ret = AppSpawnReqMsgAddStringInfo(reqHandle, name, value);
243 APPSPAWN_CHECK(ret == 0, return ret, "Failed to add ext name %{public}s", name);
244 }
245
246 // 添加一个二进制的扩展元素
247 AppDacInfo dacInfo{};
248 dacInfo.uid = 101; // 101 test data
249 dacInfo.gid = 101; // 101 test data
250 dacInfo.gidTable[0] = 101; // 101 test data
251 dacInfo.gidCount = 1;
252 (void)strcpy_s(dacInfo.userName, sizeof(dacInfo.userName), processName_.c_str());
253 ret = AppSpawnReqMsgAddExtInfo(reqHandle,
254 "app-dac-info", reinterpret_cast<uint8_t *>(&dacInfo), sizeof(dacInfo));
255 APPSPAWN_CHECK(ret == 0, return ret, "Failed to add ext name app-info");
256 return ret;
257 }
258
BuildMsgFromJson(const cJSON * appInfoConfig,AppSpawnReqMsgHandle reqHandle)259 int AppSpawnTestCommander::BuildMsgFromJson(const cJSON *appInfoConfig, AppSpawnReqMsgHandle reqHandle)
260 {
261 int ret = AddBundleInfoFromJson(appInfoConfig, reqHandle);
262 APPSPAWN_CHECK(ret == 0, return ret, "Failed to add dac %{public}s", processName_.c_str());
263
264 ret = AddDomainInfoFromJson(appInfoConfig, reqHandle);
265 APPSPAWN_CHECK(ret == 0, return ret, "Failed to add dac %{public}s", processName_.c_str());
266
267 ret = AddDacInfoFromJson(appInfoConfig, reqHandle);
268 APPSPAWN_CHECK(ret == 0, return ret, "Failed to add dac %{public}s", processName_.c_str());
269
270 ret = AddAccessTokenFromJson(appInfoConfig, reqHandle);
271 APPSPAWN_CHECK(ret == 0, return ret, "Failed to add access token %{public}s", processName_.c_str());
272
273 cJSON *obj = cJSON_GetObjectItemCaseSensitive(appInfoConfig, "permission");
274 if (obj != nullptr && cJSON_IsArray(obj)) {
275 int count = cJSON_GetArraySize(obj);
276 for (int i = 0; i < count; i++) {
277 char *value = cJSON_GetStringValue(cJSON_GetArrayItem(obj, i));
278 APPSPAWN_LOGV("permission %{public}s ", value);
279 ret = AppSpawnReqMsgAddPermission(reqHandle, value);
280 APPSPAWN_CHECK(ret == 0, return ret, "Failed to permission %{public}s", value);
281 }
282 }
283
284 ret = AddInternetPermissionInfoFromJson(appInfoConfig, reqHandle);
285 APPSPAWN_CHECK(ret == 0, return ret, "Failed to internet info %{public}s", processName_.c_str());
286
287 std::string ownerId = GetStringFromJsonObj(appInfoConfig, "owner-id");
288 if (!ownerId.empty()) {
289 ret = AppSpawnReqMsgSetAppOwnerId(reqHandle, ownerId.c_str());
290 APPSPAWN_CHECK(ret == 0, return ret, "Failed to ownerid %{public}s", processName_.c_str());
291 }
292
293 std::string renderCmd = GetStringFromJsonObj(appInfoConfig, "render-cmd");
294 if (!renderCmd.empty()) {
295 ret = AppSpawnReqMsgAddStringInfo(reqHandle, MSG_EXT_NAME_RENDER_CMD, renderCmd.c_str());
296 APPSPAWN_CHECK(ret == 0, return -1, "Failed to add renderCmd %{public}s", renderCmd.c_str());
297 }
298 return AddExtTlv(appInfoConfig, reqHandle);
299 }
300
CreateOtherMsg(AppSpawnReqMsgHandle & reqHandle,pid_t pid)301 int AppSpawnTestCommander::CreateOtherMsg(AppSpawnReqMsgHandle &reqHandle, pid_t pid)
302 {
303 if (msgType_ == MSG_GET_RENDER_TERMINATION_STATUS) {
304 int ret = AppSpawnTerminateMsgCreate(pid, &reqHandle);
305 APPSPAWN_CHECK(ret == 0, return ret, "Failed to termination message req %{public}s", processName_.c_str());
306 }
307 if (msgType_ == MSG_DUMP) {
308 int ret = AppSpawnReqMsgCreate(static_cast<AppSpawnMsgType>(msgType_), processName_.c_str(), &reqHandle);
309 APPSPAWN_CHECK(ret == 0, return ret, "Failed to dump req %{public}s", processName_.c_str());
310 ret = AppSpawnReqMsgAddStringInfo(reqHandle, "pty-name", ptyName_.c_str());
311 APPSPAWN_CHECK(ret == 0, return -1, "Failed to add ptyName_ %{public}s", ptyName_.c_str());
312 }
313 return 0;
314 }
315
GetMsgTypeFromJson(const cJSON * json)316 static uint32_t GetMsgTypeFromJson(const cJSON *json)
317 {
318 const char *msgType = GetStringFromJsonObj(json, "msg-type");
319 if (msgType == nullptr) {
320 return MSG_APP_SPAWN;
321 }
322 if (strcmp(msgType, "MSG_SPAWN_NATIVE_PROCESS") == 0) {
323 return MSG_SPAWN_NATIVE_PROCESS;
324 }
325 if (strcmp(msgType, "MSG_GET_RENDER_TERMINATION_STATUS") == 0) {
326 return MSG_GET_RENDER_TERMINATION_STATUS;
327 }
328 if (strcmp(msgType, "MSG_DUMP") == 0) {
329 return MSG_DUMP;
330 }
331 return MSG_APP_SPAWN;
332 }
333
CreateMsg(AppSpawnReqMsgHandle & reqHandle,const char * defaultConfig,uint32_t defMsgType)334 int AppSpawnTestCommander::CreateMsg(AppSpawnReqMsgHandle &reqHandle,
335 const char *defaultConfig, uint32_t defMsgType)
336 {
337 int ret = APPSPAWN_SYSTEM_ERROR;
338 if (clientHandle_ == NULL) {
339 ret = AppSpawnClientInit(appSpawn_ == 1 ? APPSPAWN_SERVER_NAME : (appSpawn_ == 2 ? NATIVESPAWN_SERVER_NAME :
340 (appSpawn_ == 3 ? HYBRIDSPAWN_SERVER_NAME : NWEBSPAWN_SERVER_NAME)), &clientHandle_);
341 }
342 reqHandle = INVALID_REQ_HANDLE;
343 if (appInfoConfig_) {
344 cJSON_Delete(appInfoConfig_);
345 appInfoConfig_ = nullptr;
346 }
347 if (!testFileName_.empty()) {
348 appInfoConfig_ = GetJsonObjFromFile(testFileName_.c_str());
349 if (appInfoConfig_ == nullptr) {
350 printf("Failed to load file %s, so use default info \n", testFileName_.c_str());
351 }
352 }
353 if (appInfoConfig_ == nullptr) {
354 appInfoConfig_ = cJSON_Parse(defaultConfig);
355 }
356 if (appInfoConfig_ == nullptr) {
357 printf("Invalid app info \n");
358 return APPSPAWN_SYSTEM_ERROR;
359 }
360 processName_ = GetStringFromJsonObj(appInfoConfig_, "process-name");
361 if (processName_.empty()) {
362 processName_ = "com.example.myapplication";
363 }
364 msgType_ = (msgType_ == MAX_TYPE_INVALID) ? GetMsgTypeFromJson(appInfoConfig_) : msgType_;
365 msgType_ = (defMsgType != MAX_TYPE_INVALID) ? defMsgType : msgType_;
366 if (msgType_ == MSG_DUMP) {
367 return CreateOtherMsg(reqHandle, 0);
368 } else if (msgType_ == MSG_GET_RENDER_TERMINATION_STATUS) {
369 pid_t pid = GetIntValueFromJsonObj(appInfoConfig_, "pid", 0);
370 return CreateOtherMsg(reqHandle, pid);
371 }
372 ret = AppSpawnReqMsgCreate(static_cast<AppSpawnMsgType>(msgType_), processName_.c_str(), &reqHandle);
373 APPSPAWN_CHECK(ret == 0, return ret, "Failed to create req %{public}s", processName_.c_str());
374
375 uint32_t msgFlags[64] = {}; // 64
376 uint32_t count = GetUint32ArrayFromJson(appInfoConfig_, "msg-flags", msgFlags, ARRAY_LENGTH(msgFlags));
377 for (uint32_t j = 0; j < count; j++) {
378 (void)AppSpawnReqMsgSetAppFlag(reqHandle, static_cast<AppFlagsIndex>(msgFlags[j]));
379 }
380 (void)AppSpawnReqMsgSetAppFlag(reqHandle, APP_FLAGS_IGNORE_SANDBOX);
381 ret = BuildMsgFromJson(appInfoConfig_, reqHandle);
382 APPSPAWN_CHECK(ret == 0, AppSpawnReqMsgFree(reqHandle);
383 return ret, "Failed to build req %{public}s", processName_.c_str());
384 return ret;
385 }
386
SendMsg()387 int AppSpawnTestCommander::SendMsg()
388 {
389 const char *server = appSpawn_ == 1 ? APPSPAWN_SERVER_NAME : (appSpawn_ == 2 ? NATIVESPAWN_SERVER_NAME :
390 (appSpawn_ == 3 ? HYBRIDSPAWN_SERVER_NAME : NWEBSPAWN_SERVER_NAME));
391 printf("Send msg to server '%s' \n", server);
392 AppSpawnReqMsgHandle reqHandle = INVALID_REQ_HANDLE;
393 int ret = 0;
394 if (msgType_ == MSG_DUMP) {
395 while (!dumpFlags) {
396 usleep(20000); // 20000
397 }
398 ret = CreateOtherMsg(reqHandle, 0);
399 } else if (msgType_ == MSG_GET_RENDER_TERMINATION_STATUS) {
400 ret = CreateOtherMsg(reqHandle, terminatePid_);
401 } else {
402 ret = CreateMsg(reqHandle, g_defaultAppInfo.c_str());
403 }
404 AppSpawnResult result = {ret, 0};
405 if (ret == 0) {
406 ret = AppSpawnClientSendMsg(clientHandle_, reqHandle, &result);
407 }
408 switch (msgType_) {
409 case MSG_APP_SPAWN:
410 if (result.result == 0) {
411 printf("Spawn app %s success, pid %d \n", processName_.c_str(), result.pid);
412 } else {
413 printf("Spawn app %s fail, result 0x%x \n", processName_.c_str(), result.result);
414 }
415 break;
416 case MSG_SPAWN_NATIVE_PROCESS:
417 if (result.result == 0) {
418 printf("Spawn native app %s success, pid %d \n", processName_.c_str(), result.pid);
419 } else {
420 printf("Spawn native app %s fail, result 0x%x \n", processName_.c_str(), result.result);
421 }
422 break;
423 case MSG_GET_RENDER_TERMINATION_STATUS:
424 printf("Terminate app %s success, pid %d status 0x%x \n",
425 processName_.c_str(), result.pid, result.result);
426 break;
427 default:
428 printf("Dump server %s result %d \n", server, ret);
429 break;
430 }
431 msgType_ = MAX_TYPE_INVALID;
432 terminatePid_ = 0;
433 printf("Please input cmd: \n");
434 return 0;
435 }
436
StartSendMsg()437 int AppSpawnTestCommander::StartSendMsg()
438 {
439 int ret = 0;
440 printf("Start send msg thread count %d file name %s \n", threadCount_, testFileName_.c_str());
441 if (threadCount_ == 1) {
442 SendMsg();
443 } else {
444 ThreadTaskHandle taskHandle = 0;
445 ret = ThreadMgrAddTask(threadMgr_, &taskHandle);
446 APPSPAWN_CHECK(ret == 0, return 0, "Failed to add task ");
447 for (uint32_t index = 0; index < threadCount_; index++) {
448 ThreadMgrAddExecutor(threadMgr_, taskHandle, TaskExecutorProc, reinterpret_cast<ThreadContext *>(this));
449 }
450 TaskSyncExecute(threadMgr_, taskHandle);
451 }
452 return 0;
453 }
454
TaskExecutorProc(ThreadTaskHandle handle,const ThreadContext * context)455 void AppSpawnTestCommander::TaskExecutorProc(ThreadTaskHandle handle, const ThreadContext *context)
456 {
457 AppSpawnTestCommander *testCmder = AppSpawnTestCommander::ConvertTo(context);
458 testCmder->SendMsg();
459 }
460
SendTaskFinish(ThreadTaskHandle handle,const ThreadContext * context)461 void AppSpawnTestCommander::SendTaskFinish(ThreadTaskHandle handle, const ThreadContext *context)
462 {
463 APPSPAWN_LOGV("SendTaskFinish %{public}u \n", handle);
464 }
465
466 static std::vector<std::string> g_args;
HandleSplitString(const char * str,void * context)467 static int HandleSplitString(const char *str, void *context)
468 {
469 APPSPAWN_LOGV("HandleSplitString %{public}s ", str);
470 std::string value = str;
471 g_args.push_back(value);
472 return 0;
473 }
474
ProcessInputCmd(std::string & cmd)475 int AppSpawnTestCommander::ProcessInputCmd(std::string &cmd)
476 {
477 g_args.clear();
478 int ret = StringSplit(cmd.c_str(), " ", nullptr, HandleSplitString);
479 std::vector<char *> options;
480 for (const auto &arg : g_args) {
481 if (!arg.empty()) {
482 options.push_back(const_cast<char *>(arg.c_str()));
483 }
484 }
485 (void)ProcessArgs(options.size(), options.data());
486 StartSendMsg();
487 return ret;
488 }
489
InputThread(ThreadTaskHandle handle,const ThreadContext * context)490 void AppSpawnTestCommander::InputThread(ThreadTaskHandle handle, const ThreadContext *context)
491 {
492 AppSpawnTestCommander *testCmder = AppSpawnTestCommander::ConvertTo(context);
493 char buffer[1024] = {0}; // 1024 test buffer max len
494 fd_set fds;
495 printf("Please input cmd: \n");
496 while (1) {
497 FD_ZERO(&fds);
498 FD_SET(STDIN_FILENO, &fds);
499 int ret = select(STDIN_FILENO + 1, &fds, nullptr, nullptr, nullptr);
500 if (ret <= 0) {
501 if (testCmder->exit_) {
502 break;
503 }
504 continue;
505 }
506 ssize_t rlen = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
507 if (rlen <= 1) {
508 continue;
509 }
510 buffer[rlen - 1] = 0;
511 printf("Recv command: '%s' \n", buffer);
512 if (strncmp("quit", buffer, strlen("quit")) == 0) {
513 testCmder->exit_ = 1;
514 break;
515 }
516 if (strncmp("send", buffer, 4) == 0) { // 4 strlen("send")
517 std::string cmd(buffer);
518 testCmder->ProcessInputCmd(cmd);
519 printf("Please input cmd: \n");
520 }
521 }
522 }
523
DumpThread(ThreadTaskHandle handle,const ThreadContext * context)524 void AppSpawnTestCommander::DumpThread(ThreadTaskHandle handle, const ThreadContext *context)
525 {
526 AppSpawnTestCommander *testCmder = AppSpawnTestCommander::ConvertTo(context);
527 printf("Start dump thread \n");
528 char buffer[10240] = {0}; // 1024 test buffer max len
529 fd_set fds;
530 while (1) {
531 testCmder->dumpFlags = 1;
532 FD_ZERO(&fds);
533 FD_SET(testCmder->ptyFd_, &fds);
534 int ret = select(testCmder->ptyFd_ + 1, &fds, nullptr, nullptr, nullptr);
535 if (ret <= 0) {
536 if (testCmder->exit_) {
537 break;
538 }
539 continue;
540 }
541 if (!FD_ISSET(testCmder->ptyFd_, &fds)) {
542 continue;
543 }
544 ssize_t rlen = read(testCmder->ptyFd_, buffer, sizeof(buffer) - 1);
545 while (rlen > 0) {
546 buffer[rlen] = '\0';
547 printf("%s", buffer);
548 fflush(stdout);
549 rlen = read(testCmder->ptyFd_, buffer, sizeof(buffer) - 1);
550 }
551 }
552 }
553
Run()554 int AppSpawnTestCommander::Run()
555 {
556 int ret = 0;
557 const char *name = appSpawn_ == 1 ? APPSPAWN_SERVER_NAME : (appSpawn_ == 2 ? NATIVESPAWN_SERVER_NAME :
558 (appSpawn_ == 3 ? HYBRIDSPAWN_SERVER_NAME : NWEBSPAWN_SERVER_NAME));
559 if (clientHandle_ == NULL) {
560 ret = AppSpawnClientInit(name, &clientHandle_);
561 APPSPAWN_CHECK(ret == 0, return -1, "Failed to create client %{public}s", name);
562 }
563
564 InitPtyInterface();
565
566 ret = CreateThreadMgr(5, &threadMgr_); // 5 max thread
567 APPSPAWN_CHECK(ret == 0, return -1, "Failed to create thread manager");
568
569 ret = ThreadMgrAddTask(threadMgr_, &inputHandle_);
570 APPSPAWN_CHECK(ret == 0, return 0, "Failed to add task for thread ");
571 ThreadMgrAddExecutor(threadMgr_, inputHandle_, InputThread, this);
572 TaskExecute(threadMgr_, inputHandle_, SendTaskFinish, this);
573
574 ret = ThreadMgrAddTask(threadMgr_, &dumpHandle_);
575 APPSPAWN_CHECK(ret == 0, return 0, "Failed to add task for thread ");
576 ThreadMgrAddExecutor(threadMgr_, dumpHandle_, DumpThread, this);
577 TaskExecute(threadMgr_, dumpHandle_, SendTaskFinish, this);
578
579 StartSendMsg();
580
581 APPSPAWN_LOGV("Finish send msg \n");
582 while (!exit_) {
583 usleep(200000); // 200000 200ms
584 }
585 ThreadMgrCancelTask(threadMgr_, inputHandle_);
586 ThreadMgrCancelTask(threadMgr_, dumpHandle_);
587 DestroyThreadMgr(threadMgr_);
588 threadMgr_ = nullptr;
589 inputHandle_ = 0;
590 dumpHandle_ = 0;
591 AppSpawnClientDestroy(clientHandle_);
592 clientHandle_ = nullptr;
593 return 0;
594 }
595
InitPtyInterface()596 int AppSpawnTestCommander::InitPtyInterface()
597 {
598 // open master pty and get slave pty
599 int pfd = open("/dev/ptmx", O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
600 APPSPAWN_CHECK(pfd >= 0, return -1, "Failed open pty err=%{public}d", errno);
601 APPSPAWN_CHECK(grantpt(pfd) >= 0, close(pfd); return -1, "Failed to call grantpt");
602 APPSPAWN_CHECK(unlockpt(pfd) >= 0, close(pfd); return -1, "Failed to call unlockpt");
603 char ptsbuffer[PTY_PATH_SIZE] = {0};
604 int ret = ptsname_r(pfd, ptsbuffer, sizeof(ptsbuffer));
605 APPSPAWN_CHECK(ret >= 0, close(pfd);
606 return -1, "Failed to get pts name err=%{public}d", errno);
607 APPSPAWN_LOGI("ptsbuffer is %{public}s", ptsbuffer);
608 APPSPAWN_CHECK(chmod(ptsbuffer, S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) == 0, close(pfd);
609 return -1, "Failed to chmod %{public}s, err=%{public}d", ptsbuffer, errno);
610 ptyFd_ = pfd;
611 ptyName_ = std::string(ptsbuffer);
612 return 0;
613 }
614 } // namespace AppSpawnModuleTest
615 } // namespace OHOS
616