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
42namespace OHOS {
43namespace AppSpawnModuleTest {
44static 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
84static 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
94int 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
146uint32_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
169int 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
181int 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
198int 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
209int 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
218int 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
230int 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
260int 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
302int 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
317static 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
335int 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
388int 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
438int 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
456void AppSpawnTestCommander::TaskExecutorProc(ThreadTaskHandle handle, const ThreadContext *context)
457{
458    AppSpawnTestCommander *testCmder = AppSpawnTestCommander::ConvertTo(context);
459    testCmder->SendMsg();
460}
461
462void AppSpawnTestCommander::SendTaskFinish(ThreadTaskHandle handle, const ThreadContext *context)
463{
464    APPSPAWN_LOGV("SendTaskFinish %{public}u \n", handle);
465}
466
467static std::vector<std::string> g_args;
468static 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
476int 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
491void 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
525void 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
555int 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
597int 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