• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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