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 27CallbackSendMsgProcess g_sendMsg = NULL; 28 29CONTROL_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 49CONTROL_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 75CONTROL_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 80CONTROL_FD_STATIC void CmdOnConnectComplete(const TaskHandle client) 81{ 82 BEGET_LOGI("[control_fd] CmdOnConnectComplete"); 83} 84 85CONTROL_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 95CONTROL_FD_STATIC void CmdDisConnectComplete(const TaskHandle client) 96{ 97 BEGET_LOGI("[control_fd] CmdDisConnectComplete"); 98} 99 100CONTROL_FD_STATIC void CmdOnSendMessageComplete(const TaskHandle task, const BufferHandle handle) 101{ 102 BEGET_LOGI("[control_fd] CmdOnSendMessageComplete"); 103} 104 105CONTROL_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 129CONTROL_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 155int 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 205void 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