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