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