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