1 /*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 * to endorse or promote products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "telnet_dev.h"
33 #ifdef LOSCFG_NET_TELNET
34 #include "unistd.h"
35 #include "stdlib.h"
36 #include "sys/ioctl.h"
37 #include "sys/types.h"
38 #include "pthread.h"
39
40 #include "los_printf.h"
41 #ifdef LOSCFG_BASE_CORE_SWTMR_ENABLE
42 #include "los_swtmr_pri.h"
43 #endif
44 #include "los_sched_pri.h"
45 #include "console.h"
46 #include "lwip/opt.h"
47 #include "lwip/sockets.h"
48 #include "telnet_pri.h"
49
50 #include "fs/driver.h"
51
52 /* event: there are more commands left in the FIFO to run */
53 #define TELNET_EVENT_MORE_CMD 0x01
54 #define TELNET_DEV_DRV_MODE 0666
55
56 STATIC TELNET_DEV_S g_telnetDev;
57 STATIC EVENT_CB_S *g_event;
58 STATIC struct Vnode *g_currentVnode;
59
GetTelnetDevByFile(const struct file * file,BOOL isOpenOp)60 STATIC INLINE TELNET_DEV_S *GetTelnetDevByFile(const struct file *file, BOOL isOpenOp)
61 {
62 struct Vnode *telnetInode = NULL;
63 TELNET_DEV_S *telnetDev = NULL;
64
65 if (file == NULL) {
66 return NULL;
67 }
68 telnetInode = file->f_vnode;
69 if (telnetInode == NULL) {
70 return NULL;
71 }
72 /*
73 * Check if the f_vnode is valid here for non-open ops (open is supposed to get invalid f_vnode):
74 * when telnet is disconnected, there still may be 'TelentShellTask' tasks trying to write
75 * to the file, but the file has illegal f_vnode because the file is used by others.
76 */
77 if (!isOpenOp) {
78 if (telnetInode != g_currentVnode) {
79 return NULL;
80 }
81 }
82 telnetDev = (TELNET_DEV_S *)((struct drv_data*)telnetInode->data)->priv;
83 return telnetDev;
84 }
85
86 /*
87 * Description : When receive user's input commands, first copy commands to the FIFO of the telnet device.
88 * Then, notify a command resolver task (an individual shell task) to take out and run commands.
89 * Return : -1 --- On failure
90 * Non-negative integer --- length of written commands
91 */
TelnetTx(const CHAR * buf,UINT32 bufLen)92 INT32 TelnetTx(const CHAR *buf, UINT32 bufLen)
93 {
94 UINT32 i;
95 TELNET_DEV_S *telnetDev = NULL;
96
97 TelnetLock();
98
99 telnetDev = &g_telnetDev;
100 if ((buf == NULL) || (telnetDev->cmdFifo == NULL)) {
101 TelnetUnlock();
102 return -1;
103 }
104
105 /* size limited */
106 if (bufLen > telnetDev->cmdFifo->fifoNum) {
107 bufLen = telnetDev->cmdFifo->fifoNum;
108 }
109
110 if (bufLen == 0) {
111 TelnetUnlock();
112 return 0;
113 }
114
115 /* copy commands to the fifo of the telnet device */
116 for (i = 0; i < bufLen; i++) {
117 telnetDev->cmdFifo->rxBuf[telnetDev->cmdFifo->rxIndex] = *buf;
118 telnetDev->cmdFifo->rxIndex++;
119 telnetDev->cmdFifo->rxIndex %= FIFO_MAX;
120 buf++;
121 }
122 telnetDev->cmdFifo->fifoNum -= bufLen;
123
124 if (telnetDev->eventPend) {
125 /* signal that there are some works to do */
126 (VOID)LOS_EventWrite(&telnetDev->eventTelnet, TELNET_EVENT_MORE_CMD);
127 }
128 /* notify the command resolver task */
129 notify_poll(&telnetDev->wait);
130 TelnetUnlock();
131
132 return (INT32)bufLen;
133 }
134
135 /*
136 * Description : When open the telnet device, init the FIFO, wait queue etc.
137 */
TelnetOpen(struct file * file)138 STATIC INT32 TelnetOpen(struct file *file)
139 {
140 struct wait_queue_head *wait = NULL;
141 TELNET_DEV_S *telnetDev = NULL;
142
143 TelnetLock();
144
145 telnetDev = GetTelnetDevByFile(file, TRUE);
146 if (telnetDev == NULL) {
147 TelnetUnlock();
148 return -1;
149 }
150
151 if (telnetDev->cmdFifo == NULL) {
152 wait = &telnetDev->wait;
153 (VOID)LOS_EventInit(&telnetDev->eventTelnet);
154 g_event = &telnetDev->eventTelnet;
155 telnetDev->cmdFifo = (TELNTE_FIFO_S *)malloc(sizeof(TELNTE_FIFO_S));
156 if (telnetDev->cmdFifo == NULL) {
157 TelnetUnlock();
158 return -1;
159 }
160 (VOID)memset_s(telnetDev->cmdFifo, sizeof(TELNTE_FIFO_S), 0, sizeof(TELNTE_FIFO_S));
161 telnetDev->cmdFifo->fifoNum = FIFO_MAX;
162 LOS_ListInit(&wait->poll_queue);
163 }
164 g_currentVnode = file->f_vnode;
165 TelnetUnlock();
166 return 0;
167 }
168
169 /*
170 * Description : When close the telnet device, free the FIFO, wait queue etc.
171 */
TelnetClose(struct file * file)172 STATIC INT32 TelnetClose(struct file *file)
173 {
174 struct wait_queue_head *wait = NULL;
175 TELNET_DEV_S *telnetDev = NULL;
176
177 TelnetLock();
178
179 telnetDev = GetTelnetDevByFile(file, FALSE);
180 if (telnetDev != NULL) {
181 wait = &telnetDev->wait;
182 LOS_ListDelete(&wait->poll_queue);
183 free(telnetDev->cmdFifo);
184 telnetDev->cmdFifo = NULL;
185 (VOID)LOS_EventDestroy(&telnetDev->eventTelnet);
186 g_event = NULL;
187 }
188 g_currentVnode = NULL;
189 TelnetUnlock();
190 return 0;
191 }
192
193 /*
194 * Description : When a command resolver task tries to read the telnet device,
195 * this method is called, and it will take out user's commands from the FIFO to run.
196 * Return : -1 --- On failure
197 * Non-negative integer --- length of commands taken out from the FIFO of the telnet device.
198 */
TelnetRead(struct file * file,CHAR * buf,size_t bufLen)199 STATIC ssize_t TelnetRead(struct file *file, CHAR *buf, size_t bufLen)
200 {
201 UINT32 i;
202 TELNET_DEV_S *telnetDev = NULL;
203
204 TelnetLock();
205
206 telnetDev = GetTelnetDevByFile(file, FALSE);
207 if ((buf == NULL) || (telnetDev == NULL) || (telnetDev->cmdFifo == NULL)) {
208 TelnetUnlock();
209 return -1;
210 }
211
212 if (telnetDev->eventPend) {
213 TelnetUnlock();
214 (VOID)LOS_EventRead(g_event, TELNET_EVENT_MORE_CMD, LOS_WAITMODE_OR, LOS_WAIT_FOREVER);
215 TelnetLock();
216 }
217
218 if (bufLen > (FIFO_MAX - telnetDev->cmdFifo->fifoNum)) {
219 bufLen = FIFO_MAX - telnetDev->cmdFifo->fifoNum;
220 }
221
222 for (i = 0; i < bufLen; i++) {
223 *buf++ = telnetDev->cmdFifo->rxBuf[telnetDev->cmdFifo->rxOutIndex++];
224 if (telnetDev->cmdFifo->rxOutIndex >= FIFO_MAX) {
225 telnetDev->cmdFifo->rxOutIndex = 0;
226 }
227 }
228 telnetDev->cmdFifo->fifoNum += bufLen;
229 /* check if no more commands left to run */
230 if (telnetDev->cmdFifo->fifoNum == FIFO_MAX) {
231 (VOID)LOS_EventClear(&telnetDev->eventTelnet, ~TELNET_EVENT_MORE_CMD);
232 }
233
234 TelnetUnlock();
235 return (ssize_t)bufLen;
236 }
237
238 /*
239 * Description : When a command resolver task tries to write command results to the telnet device,
240 * just use lwIP send function to send out results.
241 * Return : -1 --- buffer is NULL
242 * Non-negative integer --- length of written data, maybe 0.
243 */
TelnetWrite(struct file * file,const CHAR * buf,const size_t bufLen)244 STATIC ssize_t TelnetWrite(struct file *file, const CHAR *buf, const size_t bufLen)
245 {
246 INT32 ret = 0;
247 TELNET_DEV_S *telnetDev = NULL;
248
249 TelnetLock();
250
251 telnetDev = GetTelnetDevByFile(file, FALSE);
252 if ((buf == NULL) || (telnetDev == NULL) || (telnetDev->cmdFifo == NULL)) {
253 TelnetUnlock();
254 return -1;
255 }
256
257 if (OS_INT_ACTIVE) {
258 TelnetUnlock();
259 return ret;
260 }
261
262 if (!OsPreemptable()) {
263 TelnetUnlock();
264 return ret;
265 }
266
267 if (telnetDev->clientFd != 0) {
268 #ifdef LOSCFG_BASE_CORE_SWTMR_ENABLE
269 /* DO NOT call blocking API in software timer task */
270 if (OsIsSwtmrTask(OsCurrTaskGet())) {
271 TelnetUnlock();
272 return ret;
273 }
274 #endif
275 ret = send(telnetDev->clientFd, buf, bufLen, 0);
276 }
277 TelnetUnlock();
278 return ret;
279 }
280
TelnetIoctl(struct file * file,const INT32 cmd,unsigned long arg)281 STATIC INT32 TelnetIoctl(struct file *file, const INT32 cmd, unsigned long arg)
282 {
283 TELNET_DEV_S *telnetDev = NULL;
284
285 TelnetLock();
286
287 telnetDev = GetTelnetDevByFile(file, FALSE);
288 if (telnetDev == NULL) {
289 TelnetUnlock();
290 return -1;
291 }
292
293 if (cmd == CFG_TELNET_EVENT_PEND) {
294 if (arg == 0) {
295 telnetDev->eventPend = FALSE;
296 (VOID)LOS_EventWrite(&(telnetDev->eventTelnet), TELNET_EVENT_MORE_CMD);
297 (VOID)LOS_EventClear(&(telnetDev->eventTelnet), ~TELNET_EVENT_MORE_CMD);
298 } else {
299 telnetDev->eventPend = TRUE;
300 }
301 } else if (cmd == CFG_TELNET_SET_FD) {
302 if (arg >= (FD_SETSIZE - 1)) {
303 TelnetUnlock();
304 return -1;
305 }
306 telnetDev->clientFd = (INT32)arg;
307 }
308 TelnetUnlock();
309 return 0;
310 }
311
TelnetPoll(struct file * file,poll_table * table)312 STATIC INT32 TelnetPoll(struct file *file, poll_table *table)
313 {
314 TELNET_DEV_S *telnetDev = NULL;
315
316 TelnetLock();
317
318 telnetDev = GetTelnetDevByFile(file, FALSE);
319 if ((telnetDev == NULL) || (telnetDev->cmdFifo == NULL)) {
320 TelnetUnlock();
321 return -1;
322 }
323
324 poll_wait(file, &telnetDev->wait, table);
325
326 /* check if there are some commands to run */
327 if (telnetDev->cmdFifo->fifoNum != FIFO_MAX) {
328 TelnetUnlock();
329 return POLLIN | POLLRDNORM;
330 }
331 TelnetUnlock();
332 return 0;
333 }
334
335 STATIC const struct file_operations_vfs g_telnetOps = {
336 TelnetOpen,
337 TelnetClose,
338 TelnetRead,
339 TelnetWrite,
340 NULL,
341 TelnetIoctl,
342 NULL,
343 #ifndef CONFIG_DISABLE_POLL
344 TelnetPoll,
345 #endif
346 NULL,
347 };
348
349 /* Once the telnet server stopped, remove the telnet device file. */
TelnetedUnregister(VOID)350 INT32 TelnetedUnregister(VOID)
351 {
352 free(g_telnetDev.cmdFifo);
353 g_telnetDev.cmdFifo = NULL;
354 (VOID)unregister_driver(TELNET);
355
356 return 0;
357 }
358
359 /* Once the telnet server started, setup the telnet device file. */
TelnetedRegister(VOID)360 INT32 TelnetedRegister(VOID)
361 {
362 INT32 ret;
363
364 g_telnetDev.id = 0;
365 g_telnetDev.cmdFifo = NULL;
366 g_telnetDev.eventPend = TRUE;
367
368 ret = register_driver(TELNET, &g_telnetOps, TELNET_DEV_DRV_MODE, &g_telnetDev);
369 if (ret != 0) {
370 PRINT_ERR("Telnet register driver error.\n");
371 }
372 return ret;
373 }
374
375 /* When a telnet client connection established, update the output console for tasks. */
TelnetDevInit(INT32 clientFd)376 INT32 TelnetDevInit(INT32 clientFd)
377 {
378 INT32 ret;
379
380 if (clientFd < 0) {
381 PRINT_ERR("Invalid telnet clientFd.\n");
382 return -1;
383 }
384 ret = system_console_init(TELNET);
385 if (ret != 0) {
386 PRINT_ERR("Telnet console init error.\n");
387 return ret;
388 }
389 ret = ioctl(STDIN_FILENO, CFG_TELNET_SET_FD, clientFd);
390 if (ret != 0) {
391 PRINT_ERR("Telnet device ioctl error.\n");
392 (VOID)system_console_deinit(TELNET);
393 }
394 return ret;
395 }
396
397 /* When closing the telnet client connection, reset the output console for tasks. */
TelnetDevDeinit(VOID)398 INT32 TelnetDevDeinit(VOID)
399 {
400 INT32 ret;
401
402 ret = system_console_deinit(TELNET);
403 if (ret != 0) {
404 PRINT_ERR("Telnet console deinit error.\n");
405 }
406 return ret;
407 }
408 #endif
409
410