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