1d9f0492fSopenharmony_ci/*
2d9f0492fSopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd.
3d9f0492fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4d9f0492fSopenharmony_ci * you may not use this file except in compliance with the License.
5d9f0492fSopenharmony_ci * You may obtain a copy of the License at
6d9f0492fSopenharmony_ci *
7d9f0492fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0
8d9f0492fSopenharmony_ci *
9d9f0492fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10d9f0492fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11d9f0492fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12d9f0492fSopenharmony_ci * See the License for the specific language governing permissions and
13d9f0492fSopenharmony_ci * limitations under the License.
14d9f0492fSopenharmony_ci */
15d9f0492fSopenharmony_ci#include <errno.h>
16d9f0492fSopenharmony_ci#include <fcntl.h>
17d9f0492fSopenharmony_ci#include <stdlib.h>
18d9f0492fSopenharmony_ci#include <stdio.h>
19d9f0492fSopenharmony_ci#include <sys/stat.h>
20d9f0492fSopenharmony_ci#include <termios.h>
21d9f0492fSopenharmony_ci#include <unistd.h>
22d9f0492fSopenharmony_ci
23d9f0492fSopenharmony_ci#include "beget_ext.h"
24d9f0492fSopenharmony_ci#include "control_fd.h"
25d9f0492fSopenharmony_ci#include "securec.h"
26d9f0492fSopenharmony_ci
27d9f0492fSopenharmony_ciCallbackSendMsgProcess g_sendMsg = NULL;
28d9f0492fSopenharmony_ci
29d9f0492fSopenharmony_ciCONTROL_FD_STATIC void ProcessPtyWrite(const WatcherHandle taskHandle, int fd, uint32_t *events, const void *context)
30d9f0492fSopenharmony_ci{
31d9f0492fSopenharmony_ci    if ((fd < 0) || (events == NULL) || (context == NULL)) {
32d9f0492fSopenharmony_ci        BEGET_LOGE("[control_fd] Invalid fifo write parameter");
33d9f0492fSopenharmony_ci        return;
34d9f0492fSopenharmony_ci    }
35d9f0492fSopenharmony_ci    CmdAgent *agent = (CmdAgent *)context;
36d9f0492fSopenharmony_ci    char rbuf[PTY_BUF_SIZE] = {0};
37d9f0492fSopenharmony_ci    ssize_t rlen = read(fd, rbuf, PTY_BUF_SIZE - 1);
38d9f0492fSopenharmony_ci    int ret = fflush(stdin);
39d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(ret == 0, return, "[control_fd] Failed fflush err=%d", errno);
40d9f0492fSopenharmony_ci    if (rlen > 0) {
41d9f0492fSopenharmony_ci        ssize_t wlen = write(agent->ptyFd, rbuf, rlen);
42d9f0492fSopenharmony_ci        BEGET_ERROR_CHECK(wlen == rlen, return, "[control_fd] Failed write fifo err=%d", errno);
43d9f0492fSopenharmony_ci    }
44d9f0492fSopenharmony_ci    ret = fflush(stdout);
45d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(ret == 0, return, "[control_fd] Failed fflush err=%d", errno);
46d9f0492fSopenharmony_ci    *events = EVENT_READ;
47d9f0492fSopenharmony_ci}
48d9f0492fSopenharmony_ci
49d9f0492fSopenharmony_ciCONTROL_FD_STATIC void ProcessPtyRead(const WatcherHandle taskHandle, int fd, uint32_t *events, const void *context)
50d9f0492fSopenharmony_ci{
51d9f0492fSopenharmony_ci    if ((fd < 0) || (events == NULL) || (context == NULL)) {
52d9f0492fSopenharmony_ci        BEGET_LOGE("[control_fd] Invalid fifo read parameter");
53d9f0492fSopenharmony_ci        return;
54d9f0492fSopenharmony_ci    }
55d9f0492fSopenharmony_ci    CmdAgent *agent = (CmdAgent *)context;
56d9f0492fSopenharmony_ci    char buf[PTY_BUF_SIZE] = {0};
57d9f0492fSopenharmony_ci    ssize_t readlen = 0;
58d9f0492fSopenharmony_ci    do {
59d9f0492fSopenharmony_ci        readlen = read(fd, buf, PTY_BUF_SIZE - 1);
60d9f0492fSopenharmony_ci    } while (readlen == -1 && errno == EINTR);
61d9f0492fSopenharmony_ci
62d9f0492fSopenharmony_ci    if (readlen > 0) {
63d9f0492fSopenharmony_ci        fprintf(stdout, "%s", buf);
64d9f0492fSopenharmony_ci    } else {
65d9f0492fSopenharmony_ci        (void)close(agent->ptyFd);
66d9f0492fSopenharmony_ci        LE_StopLoop(LE_GetDefaultLoop());
67d9f0492fSopenharmony_ci        *events = 0;
68d9f0492fSopenharmony_ci        return;
69d9f0492fSopenharmony_ci    }
70d9f0492fSopenharmony_ci    int ret = fflush(stdout);
71d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(ret == 0, return, "[control_fd] Failed fflush err=%d", errno);
72d9f0492fSopenharmony_ci    *events = EVENT_READ;
73d9f0492fSopenharmony_ci}
74d9f0492fSopenharmony_ci
75d9f0492fSopenharmony_ciCONTROL_FD_STATIC void CmdClientOnRecvMessage(const TaskHandle task, const uint8_t *buffer, uint32_t buffLen)
76d9f0492fSopenharmony_ci{
77d9f0492fSopenharmony_ci    BEGET_LOGI("[control_fd] CmdOnRecvMessage %s len %d.", (char *)buffer, buffLen);
78d9f0492fSopenharmony_ci}
79d9f0492fSopenharmony_ci
80d9f0492fSopenharmony_ciCONTROL_FD_STATIC void CmdOnConnectComplete(const TaskHandle client)
81d9f0492fSopenharmony_ci{
82d9f0492fSopenharmony_ci    BEGET_LOGI("[control_fd] CmdOnConnectComplete");
83d9f0492fSopenharmony_ci}
84d9f0492fSopenharmony_ci
85d9f0492fSopenharmony_ciCONTROL_FD_STATIC void CmdOnClose(const TaskHandle task)
86d9f0492fSopenharmony_ci{
87d9f0492fSopenharmony_ci    BEGET_LOGI("[control_fd] CmdOnClose");
88d9f0492fSopenharmony_ci    CmdAgent *agent = (CmdAgent *)LE_GetUserData(task);
89d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(agent != NULL, return, "[control_fd] Invalid agent");
90d9f0492fSopenharmony_ci    (void)close(agent->ptyFd);
91d9f0492fSopenharmony_ci    agent->ptyFd = -1;
92d9f0492fSopenharmony_ci    LE_StopLoop(LE_GetDefaultLoop());
93d9f0492fSopenharmony_ci}
94d9f0492fSopenharmony_ci
95d9f0492fSopenharmony_ciCONTROL_FD_STATIC void CmdDisConnectComplete(const TaskHandle client)
96d9f0492fSopenharmony_ci{
97d9f0492fSopenharmony_ci    BEGET_LOGI("[control_fd] CmdDisConnectComplete");
98d9f0492fSopenharmony_ci}
99d9f0492fSopenharmony_ci
100d9f0492fSopenharmony_ciCONTROL_FD_STATIC void CmdOnSendMessageComplete(const TaskHandle task, const BufferHandle handle)
101d9f0492fSopenharmony_ci{
102d9f0492fSopenharmony_ci    BEGET_LOGI("[control_fd] CmdOnSendMessageComplete");
103d9f0492fSopenharmony_ci}
104d9f0492fSopenharmony_ci
105d9f0492fSopenharmony_ciCONTROL_FD_STATIC CmdAgent *CmdAgentCreate(const char *server)
106d9f0492fSopenharmony_ci{
107d9f0492fSopenharmony_ci    if (server == NULL) {
108d9f0492fSopenharmony_ci        BEGET_LOGE("[control_fd] Invalid parameter");
109d9f0492fSopenharmony_ci        return NULL;
110d9f0492fSopenharmony_ci    }
111d9f0492fSopenharmony_ci    TaskHandle task = NULL;
112d9f0492fSopenharmony_ci    LE_StreamInfo info = {};
113d9f0492fSopenharmony_ci    info.baseInfo.flags = TASK_STREAM | TASK_PIPE | TASK_CONNECT;
114d9f0492fSopenharmony_ci    info.server = (char *)server;
115d9f0492fSopenharmony_ci    info.baseInfo.userDataSize = sizeof(CmdAgent);
116d9f0492fSopenharmony_ci    info.baseInfo.close = CmdOnClose;
117d9f0492fSopenharmony_ci    info.disConnectComplete = CmdDisConnectComplete;
118d9f0492fSopenharmony_ci    info.connectComplete = CmdOnConnectComplete;
119d9f0492fSopenharmony_ci    info.sendMessageComplete = CmdOnSendMessageComplete;
120d9f0492fSopenharmony_ci    info.recvMessage = CmdClientOnRecvMessage;
121d9f0492fSopenharmony_ci    LE_STATUS status = LE_CreateStreamClient(LE_GetDefaultLoop(), &task, &info);
122d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(status == 0, return NULL, "[control_fd] Failed create client");
123d9f0492fSopenharmony_ci    CmdAgent *agent = (CmdAgent *)LE_GetUserData(task);
124d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(agent != NULL, return NULL, "[control_fd] Invalid agent");
125d9f0492fSopenharmony_ci    agent->task = task;
126d9f0492fSopenharmony_ci    return agent;
127d9f0492fSopenharmony_ci}
128d9f0492fSopenharmony_ci
129d9f0492fSopenharmony_ciCONTROL_FD_STATIC int SendCmdMessage(const CmdAgent *agent, uint16_t type, const char *cmd, const char *ptyName)
130d9f0492fSopenharmony_ci{
131d9f0492fSopenharmony_ci    if ((agent == NULL) || (cmd == NULL) || (ptyName == NULL)) {
132d9f0492fSopenharmony_ci        BEGET_LOGE("[control_fd] Invalid parameter");
133d9f0492fSopenharmony_ci        return -1;
134d9f0492fSopenharmony_ci    }
135d9f0492fSopenharmony_ci    BufferHandle handle = NULL;
136d9f0492fSopenharmony_ci    uint32_t bufferSize = sizeof(CmdMessage) + strlen(cmd) + PTY_PATH_SIZE + 1;
137d9f0492fSopenharmony_ci    handle = LE_CreateBuffer(LE_GetDefaultLoop(), bufferSize);
138d9f0492fSopenharmony_ci    char *buff = (char *)LE_GetBufferInfo(handle, NULL, NULL);
139d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(buff != NULL, return -1, "[control_fd] Failed get buffer info");
140d9f0492fSopenharmony_ci    CmdMessage *message = (CmdMessage *)buff;
141d9f0492fSopenharmony_ci    message->msgSize = bufferSize;
142d9f0492fSopenharmony_ci    message->type = type;
143d9f0492fSopenharmony_ci    int ret = strcpy_s(message->ptyName, PTY_PATH_SIZE - 1, ptyName);
144d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(ret == 0, LE_FreeBuffer(LE_GetDefaultLoop(), agent->task, handle);
145d9f0492fSopenharmony_ci        return -1, "[control_fd] Failed to copy pty name %s", ptyName);
146d9f0492fSopenharmony_ci    ret = strcpy_s(message->cmd, bufferSize - sizeof(CmdMessage) - PTY_PATH_SIZE, cmd);
147d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(ret == 0, LE_FreeBuffer(LE_GetDefaultLoop(), agent->task, handle);
148d9f0492fSopenharmony_ci        return -1, "[control_fd] Failed to copy cmd %s", cmd);
149d9f0492fSopenharmony_ci    ret = LE_Send(LE_GetDefaultLoop(), agent->task, handle, bufferSize);
150d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(ret == 0, return -1, "[control_fd] Failed LE_Send msg type %d, cmd %s",
151d9f0492fSopenharmony_ci        message->type, message->cmd);
152d9f0492fSopenharmony_ci    return 0;
153d9f0492fSopenharmony_ci}
154d9f0492fSopenharmony_ci
155d9f0492fSopenharmony_ciint InitPtyInterface(CmdAgent *agent, uint16_t type, const char *cmd, CallbackSendMsgProcess callback)
156d9f0492fSopenharmony_ci{
157d9f0492fSopenharmony_ci    if ((cmd == NULL) || (agent == NULL)) {
158d9f0492fSopenharmony_ci        return -1;
159d9f0492fSopenharmony_ci    }
160d9f0492fSopenharmony_ci#ifndef STARTUP_INIT_TEST
161d9f0492fSopenharmony_ci    g_sendMsg = callback;
162d9f0492fSopenharmony_ci    // initialize terminal
163d9f0492fSopenharmony_ci    struct termios term;
164d9f0492fSopenharmony_ci    int ret = tcgetattr(STDIN_FILENO, &term);
165d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(ret == 0, return -1, "Failed tcgetattr stdin, err=%d", errno);
166d9f0492fSopenharmony_ci    cfmakeraw(&term);
167d9f0492fSopenharmony_ci    term.c_cc[VTIME] = 0;
168d9f0492fSopenharmony_ci    term.c_cc[VMIN] = 1;
169d9f0492fSopenharmony_ci    ret = tcsetattr(STDIN_FILENO, TCSANOW, &term);
170d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(ret == 0, return -1, "Failed tcsetattr term, err=%d", errno);
171d9f0492fSopenharmony_ci    // open master pty and get slave pty
172d9f0492fSopenharmony_ci    int pfd = open("/dev/ptmx", O_RDWR | O_CLOEXEC);
173d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(pfd >= 0, return -1, "Failed open pty err=%d", errno);
174d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(grantpt(pfd) >= 0, close(pfd); return -1, "Failed to call grantpt");
175d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(unlockpt(pfd) >= 0, close(pfd); return -1, "Failed to call unlockpt");
176d9f0492fSopenharmony_ci    char ptsbuffer[PTY_PATH_SIZE] = {0};
177d9f0492fSopenharmony_ci    ret = ptsname_r(pfd, ptsbuffer, sizeof(ptsbuffer));
178d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(ret >= 0, close(pfd); return -1, "Failed to get pts name err=%d", errno);
179d9f0492fSopenharmony_ci    BEGET_LOGI("ptsbuffer is %s", ptsbuffer);
180d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(chmod(ptsbuffer, S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) == 0,
181d9f0492fSopenharmony_ci        close(pfd); return -1, "Failed to chmod %s, err=%d", ptsbuffer, errno);
182d9f0492fSopenharmony_ci    agent->ptyFd = pfd;
183d9f0492fSopenharmony_ci
184d9f0492fSopenharmony_ci    LE_WatchInfo info = {};
185d9f0492fSopenharmony_ci    info.flags = 0;
186d9f0492fSopenharmony_ci    info.events = EVENT_READ;
187d9f0492fSopenharmony_ci    info.processEvent = ProcessPtyRead;
188d9f0492fSopenharmony_ci    info.fd = pfd; // read ptmx
189d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(LE_StartWatcher(LE_GetDefaultLoop(), &agent->reader, &info, agent) == LE_SUCCESS,
190d9f0492fSopenharmony_ci        close(pfd); return -1, "[control_fd] Failed le_loop start watcher ptmx read");
191d9f0492fSopenharmony_ci    info.processEvent = ProcessPtyWrite;
192d9f0492fSopenharmony_ci    info.fd = STDIN_FILENO; // read stdin and write ptmx
193d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(LE_StartWatcher(LE_GetDefaultLoop(), &agent->input, &info, agent) == LE_SUCCESS,
194d9f0492fSopenharmony_ci        close(pfd); return -1, "[control_fd] Failed le_loop start watcher stdin read and write ptmx");
195d9f0492fSopenharmony_ci    if (g_sendMsg == NULL) {
196d9f0492fSopenharmony_ci        ret = SendCmdMessage(agent, type, cmd, ptsbuffer);
197d9f0492fSopenharmony_ci    } else {
198d9f0492fSopenharmony_ci        ret = g_sendMsg(agent, type, cmd, ptsbuffer);
199d9f0492fSopenharmony_ci    }
200d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(ret == 0, close(pfd); return -1, "[control_fd] Failed send message");
201d9f0492fSopenharmony_ci#endif
202d9f0492fSopenharmony_ci    return 0;
203d9f0492fSopenharmony_ci}
204d9f0492fSopenharmony_ci
205d9f0492fSopenharmony_civoid CmdClientInit(const char *socketPath, uint16_t type, const char *cmd, CallbackSendMsgProcess callback)
206d9f0492fSopenharmony_ci{
207d9f0492fSopenharmony_ci    if ((socketPath == NULL) || (cmd == NULL)) {
208d9f0492fSopenharmony_ci        BEGET_LOGE("[control_fd] Invalid parameter");
209d9f0492fSopenharmony_ci    }
210d9f0492fSopenharmony_ci    BEGET_LOGI("[control_fd] CmdAgentInit");
211d9f0492fSopenharmony_ci    CmdAgent *agent = CmdAgentCreate(socketPath);
212d9f0492fSopenharmony_ci    BEGET_ERROR_CHECK(agent != NULL, return, "[control_fd] Failed to create agent");
213d9f0492fSopenharmony_ci#ifndef STARTUP_INIT_TEST
214d9f0492fSopenharmony_ci    int ret = InitPtyInterface(agent, type, cmd, callback);
215d9f0492fSopenharmony_ci    if (ret != 0) {
216d9f0492fSopenharmony_ci        return;
217d9f0492fSopenharmony_ci    }
218d9f0492fSopenharmony_ci    LE_RunLoop(LE_GetDefaultLoop());
219d9f0492fSopenharmony_ci    LE_CloseLoop(LE_GetDefaultLoop());
220d9f0492fSopenharmony_ci#endif
221d9f0492fSopenharmony_ci    BEGET_LOGI("Cmd Client exit ");
222d9f0492fSopenharmony_ci}
223