xref: /kernel/liteos_a/shell/full/src/base/shmsg.c (revision 0d163575)
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 "shmsg.h"
33#include "shell_pri.h"
34#include "shcmd.h"
35#include "stdlib.h"
36#include "stdio.h"
37#include "unistd.h"
38#include "securec.h"
39#include "los_base.h"
40#include "los_task.h"
41#include "los_event.h"
42#include "los_list.h"
43#include "los_printf.h"
44
45#ifdef LOSCFG_FS_VFS
46#include "console.h"
47#endif
48
49
50CHAR *ShellGetInputBuf(ShellCB *shellCB)
51{
52    CmdKeyLink *cmdkey = shellCB->cmdKeyLink;
53    CmdKeyLink *cmdNode = NULL;
54
55    (VOID)pthread_mutex_lock(&shellCB->keyMutex);
56    if ((cmdkey == NULL) || LOS_ListEmpty(&cmdkey->list)) {
57        (VOID)pthread_mutex_unlock(&shellCB->keyMutex);
58        return NULL;
59    }
60
61    cmdNode = LOS_DL_LIST_ENTRY(cmdkey->list.pstNext, CmdKeyLink, list);
62    LOS_ListDelete(&(cmdNode->list)); /* 'cmdNode' freed in history save process */
63    (VOID)pthread_mutex_unlock(&shellCB->keyMutex);
64
65    return cmdNode->cmdString;
66}
67
68STATIC VOID ShellSaveHistoryCmd(const CHAR *string, ShellCB *shellCB)
69{
70    CmdKeyLink *cmdHistory = shellCB->cmdHistoryKeyLink;
71    CmdKeyLink *cmdkey = LOS_DL_LIST_ENTRY(string, CmdKeyLink, cmdString);
72    CmdKeyLink *cmdNxt = NULL;
73
74    if ((string == NULL) || (strlen(string) == 0)) {
75        return;
76    }
77
78    (VOID)pthread_mutex_lock(&shellCB->historyMutex);
79    if (cmdHistory->count != 0) {
80        cmdNxt = LOS_DL_LIST_ENTRY(cmdHistory->list.pstPrev, CmdKeyLink, list);
81        if (strcmp(string, cmdNxt->cmdString) == 0) {
82            (VOID)LOS_MemFree(m_aucSysMem0, (VOID *)cmdkey);
83            (VOID)pthread_mutex_unlock(&shellCB->historyMutex);
84            return;
85        }
86    }
87
88    if (cmdHistory->count == CMD_HISTORY_LEN) {
89        cmdNxt = LOS_DL_LIST_ENTRY(cmdHistory->list.pstNext, CmdKeyLink, list);
90        LOS_ListDelete(&(cmdNxt->list));
91        LOS_ListTailInsert(&(cmdHistory->list), &(cmdkey->list));
92        (VOID)LOS_MemFree(m_aucSysMem0, (VOID *)cmdNxt);
93        (VOID)pthread_mutex_unlock(&shellCB->historyMutex);
94        return;
95    }
96
97    LOS_ListTailInsert(&(cmdHistory->list), &(cmdkey->list));
98    cmdHistory->count++;
99
100    (VOID)pthread_mutex_unlock(&shellCB->historyMutex);
101    return;
102}
103
104STATIC VOID ShellNotify(ShellCB *shellCB)
105{
106    (VOID)LOS_EventWrite(&shellCB->shellEvent, SHELL_CMD_PARSE_EVENT);
107}
108
109enum {
110    STAT_NORMAL_KEY,
111    STAT_ESC_KEY,
112    STAT_MULTI_KEY
113};
114
115STATIC INT32 ShellCmdLineCheckUDRL(const CHAR ch, ShellCB *shellCB)
116{
117    INT32 ret = LOS_OK;
118    if (ch == 0x1b) { /* 0x1b: ESC */
119        shellCB->shellKeyType = STAT_ESC_KEY;
120        return ret;
121    } else if (ch == 0x5b) { /* 0x5b: first Key combination */
122        if (shellCB->shellKeyType == STAT_ESC_KEY) {
123            shellCB->shellKeyType = STAT_MULTI_KEY;
124            return ret;
125        }
126    } else if (ch == 0x41) { /* up */
127        if (shellCB->shellKeyType == STAT_MULTI_KEY) {
128            OsShellHistoryShow(CMD_KEY_UP, shellCB);
129            shellCB->shellKeyType = STAT_NORMAL_KEY;
130            return ret;
131        }
132    } else if (ch == 0x42) { /* down */
133        if (shellCB->shellKeyType == STAT_MULTI_KEY) {
134            shellCB->shellKeyType = STAT_NORMAL_KEY;
135            OsShellHistoryShow(CMD_KEY_DOWN, shellCB);
136            return ret;
137        }
138    } else if (ch == 0x43) { /* right */
139        if (shellCB->shellKeyType == STAT_MULTI_KEY) {
140            shellCB->shellKeyType = STAT_NORMAL_KEY;
141            return ret;
142        }
143    } else if (ch == 0x44) { /* left */
144        if (shellCB->shellKeyType == STAT_MULTI_KEY) {
145            shellCB->shellKeyType = STAT_NORMAL_KEY;
146            return ret;
147        }
148    }
149    return LOS_NOK;
150}
151
152LITE_OS_SEC_TEXT_MINOR VOID ShellCmdLineParse(CHAR c, pf_OUTPUT outputFunc, ShellCB *shellCB)
153{
154    const CHAR ch = c;
155    INT32 ret;
156
157    if ((shellCB->shellBufOffset == 0) && (ch != '\n') && (ch != '\0')) {
158        (VOID)memset_s(shellCB->shellBuf, SHOW_MAX_LEN, 0, SHOW_MAX_LEN);
159    }
160
161    if ((ch == '\r') || (ch == '\n')) {
162        if (shellCB->shellBufOffset < (SHOW_MAX_LEN - 1)) {
163            shellCB->shellBuf[shellCB->shellBufOffset] = '\0';
164        }
165        shellCB->shellBufOffset = 0;
166        (VOID)pthread_mutex_lock(&shellCB->keyMutex);
167        OsShellCmdPush(shellCB->shellBuf, shellCB->cmdKeyLink);
168        (VOID)pthread_mutex_unlock(&shellCB->keyMutex);
169        ShellNotify(shellCB);
170        return;
171    } else if ((ch == '\b') || (ch == 0x7F)) { /* backspace or delete(0x7F) */
172        if ((shellCB->shellBufOffset > 0) && (shellCB->shellBufOffset < (SHOW_MAX_LEN - 1))) {
173            shellCB->shellBuf[shellCB->shellBufOffset - 1] = '\0';
174            shellCB->shellBufOffset--;
175            outputFunc("\b \b");
176        }
177        return;
178    } else if (ch == 0x09) { /* 0x09: tab */
179        if ((shellCB->shellBufOffset > 0) && (shellCB->shellBufOffset < (SHOW_MAX_LEN - 1))) {
180            ret = OsTabCompletion(shellCB->shellBuf, &shellCB->shellBufOffset);
181            if (ret > 1) {
182                outputFunc("OHOS # %s", shellCB->shellBuf);
183            }
184        }
185        return;
186    }
187    /* parse the up/down/right/left key */
188    ret = ShellCmdLineCheckUDRL(ch, shellCB);
189    if (ret == LOS_OK) {
190        return;
191    }
192
193    if ((ch != '\n') && (ch != '\0')) {
194        if (shellCB->shellBufOffset < (SHOW_MAX_LEN - 1)) {
195            shellCB->shellBuf[shellCB->shellBufOffset] = ch;
196        } else {
197            shellCB->shellBuf[SHOW_MAX_LEN - 1] = '\0';
198        }
199        shellCB->shellBufOffset++;
200        outputFunc("%c", ch);
201    }
202
203    shellCB->shellKeyType = STAT_NORMAL_KEY;
204}
205
206LITE_OS_SEC_TEXT_MINOR UINT32 ShellMsgTypeGet(CmdParsed *cmdParsed, const CHAR *cmdType)
207{
208    CmdItemNode *curCmdItem = (CmdItemNode *)NULL;
209    UINT32 len;
210    UINT32 minLen;
211    CmdModInfo *cmdInfo = OsCmdInfoGet();
212
213    if ((cmdParsed == NULL) || (cmdType == NULL)) {
214        return OS_INVALID;
215    }
216
217    len = strlen(cmdType);
218    LOS_DL_LIST_FOR_EACH_ENTRY(curCmdItem, &(cmdInfo->cmdList.list), CmdItemNode, list) {
219        if ((len == strlen(curCmdItem->cmd->cmdKey)) &&
220            (strncmp((CHAR *)(curCmdItem->cmd->cmdKey), cmdType, len) == 0)) {
221            minLen = (len < CMD_KEY_LEN) ? len : CMD_KEY_LEN;
222            (VOID)memcpy_s((CHAR *)(cmdParsed->cmdKeyword), CMD_KEY_LEN, cmdType, minLen);
223            cmdParsed->cmdType = curCmdItem->cmd->cmdType;
224            return LOS_OK;
225        }
226    }
227
228    return OS_INVALID;
229}
230
231STATIC UINT32 ShellMsgNameGetAndExec(CmdParsed *cmdParsed, const CHAR *output, UINT32 len)
232{
233    UINT32 loop;
234    UINT32 ret;
235    const CHAR *tmpStr = NULL;
236    BOOL quotes = FALSE;
237    CHAR *msgName = (CHAR *)LOS_MemAlloc(m_aucSysMem0, len + 1);
238    if (msgName == NULL) {
239        PRINTK("malloc failure in %s[%d]\n", __FUNCTION__, __LINE__);
240        return OS_INVALID;
241    }
242    /* Scan the 'output' string for command */
243    /* Notice: Command string must not have any special name */
244    for (tmpStr = output, loop = 0; (*tmpStr != '\0') && (loop < len);) {
245        /* If reach a double quotes, switch the quotes matching status */
246        if (*tmpStr == '\"') {
247            SWITCH_QUOTES_STATUS(quotes);
248            /* Ignore the double quote CHARactor itself */
249            tmpStr++;
250            continue;
251        }
252        /* If detected a space which the quotes matching status is false */
253        /* which said has detected the first space for separator, finish this scan operation */
254        if ((*tmpStr == ' ') && (QUOTES_STATUS_CLOSE(quotes))) {
255            break;
256        }
257        msgName[loop] = *tmpStr++;
258        loop++;
259    }
260    msgName[loop] = '\0';
261    /* Scan the command list to check whether the command can be found */
262    ret = ShellMsgTypeGet(cmdParsed, msgName);
263    PRINTK("\n");
264    if (ret != LOS_OK) {
265        PRINTK("%s:command not found", msgName);
266    } else {
267        (VOID)OsCmdExec(cmdParsed, (CHAR *)output);
268    }
269    (VOID)LOS_MemFree(m_aucSysMem0, msgName);
270    return ret;
271}
272
273LITE_OS_SEC_TEXT_MINOR UINT32 ShellMsgParse(const VOID *msg)
274{
275    CHAR *output = NULL;
276    UINT32 len, cmdLen, newLen;
277    CmdParsed cmdParsed;
278    UINT32 ret = OS_INVALID;
279    CHAR *buf = (CHAR *)msg;
280    CHAR *newMsg = NULL;
281    CHAR *cmd = "exec";
282
283    if (msg == NULL) {
284        goto END;
285    }
286
287    len = strlen(msg);
288    /* 2: strlen("./") */
289    if ((len > 2) && (buf[0] == '.') && (buf[1] == '/')) {
290        cmdLen = strlen(cmd);
291        newLen = len + 1 + cmdLen + 1;
292        newMsg = (CHAR *)LOS_MemAlloc(m_aucSysMem0, newLen);
293        if (newMsg == NULL) {
294            PRINTK("malloc failure in %s[%d]\n", __FUNCTION__, __LINE__);
295            goto END;
296        }
297        (VOID)memcpy_s(newMsg, newLen, cmd, cmdLen);
298        newMsg[cmdLen] = ' ';
299        (VOID)memcpy_s(newMsg + cmdLen + 1, newLen - cmdLen - 1,  (CHAR *)msg + 1, len);
300        msg = newMsg;
301        len = newLen - 1;
302    }
303    output = (CHAR *)LOS_MemAlloc(m_aucSysMem0, len + 1);
304    if (output == NULL) {
305        PRINTK("malloc failure in %s[%d]\n", __FUNCTION__, __LINE__);
306        goto END;
307    }
308    /* Call function 'OsCmdKeyShift' to squeeze and clear useless or overmuch space if string buffer */
309    ret = OsCmdKeyShift((CHAR *)msg, output, len + 1);
310    if ((ret != LOS_OK) || (strlen(output) == 0)) {
311        ret = OS_INVALID;
312        goto END_FREE_OUTPUT;
313    }
314
315    (VOID)memset_s(&cmdParsed, sizeof(CmdParsed), 0, sizeof(CmdParsed));
316
317    ret = ShellMsgNameGetAndExec(&cmdParsed, output, len);
318
319END_FREE_OUTPUT:
320    (VOID)LOS_MemFree(m_aucSysMem0, output);
321END:
322    if (newMsg != NULL) {
323        (VOID)LOS_MemFree(m_aucSysMem0, newMsg);
324    }
325    return ret;
326}
327
328#ifdef LOSCFG_FS_VFS
329LITE_OS_SEC_TEXT_MINOR UINT32 ShellEntry(UINTPTR param)
330{
331    CHAR ch;
332    INT32 n;
333    ShellCB *shellCB = (ShellCB *)param;
334
335    CONSOLE_CB *consoleCB = OsGetConsoleByID((INT32)shellCB->consoleID);
336    if (consoleCB == NULL) {
337        PRINT_ERR("Shell task init error!\n");
338        return 1;
339    }
340
341    (VOID)memset_s(shellCB->shellBuf, SHOW_MAX_LEN, 0, SHOW_MAX_LEN);
342
343    while (1) {
344#ifdef LOSCFG_PLATFORM_CONSOLE
345        if (!IsConsoleOccupied(consoleCB)) {
346#endif
347            /* is console ready for shell ? */
348            n = read(consoleCB->fd, &ch, 1);
349            if (n == 1) {
350                ShellCmdLineParse(ch, (pf_OUTPUT)dprintf, shellCB);
351            }
352            if (is_nonblock(consoleCB)) {
353                LOS_Msleep(50); /* 50: 50MS for sleep */
354            }
355#ifdef LOSCFG_PLATFORM_CONSOLE
356        }
357#endif
358    }
359}
360#endif
361
362STATIC VOID ShellCmdProcess(ShellCB *shellCB)
363{
364    CHAR *buf = NULL;
365    while (1) {
366        buf = ShellGetInputBuf(shellCB);
367        if (buf == NULL) {
368            break;
369        }
370        (VOID)ShellMsgParse(buf);
371        ShellSaveHistoryCmd(buf, shellCB);
372        shellCB->cmdMaskKeyLink = shellCB->cmdHistoryKeyLink;
373    }
374}
375
376LITE_OS_SEC_TEXT_MINOR UINT32 ShellTask(UINTPTR param1,
377                                        UINTPTR param2,
378                                        UINTPTR param3,
379                                        UINTPTR param4)
380{
381    UINT32 ret;
382    ShellCB *shellCB = (ShellCB *)param1;
383    (VOID)param2;
384    (VOID)param3;
385    (VOID)param4;
386
387    while (1) {
388        PRINTK("\nOHOS # ");
389        ret = LOS_EventRead(&shellCB->shellEvent,
390                            0xFFF, LOS_WAITMODE_OR | LOS_WAITMODE_CLR, LOS_WAIT_FOREVER);
391        if (ret == SHELL_CMD_PARSE_EVENT) {
392            ShellCmdProcess(shellCB);
393        } else if (ret == CONSOLE_SHELL_KEY_EVENT) {
394            break;
395        }
396    }
397    OsShellKeyDeInit((CmdKeyLink *)shellCB->cmdKeyLink);
398    OsShellKeyDeInit((CmdKeyLink *)shellCB->cmdHistoryKeyLink);
399    (VOID)LOS_EventDestroy(&shellCB->shellEvent);
400    (VOID)LOS_MemFree((VOID *)m_aucSysMem0, shellCB);
401    return 0;
402}
403
404#define SERIAL_SHELL_TASK_NAME "SerialShellTask"
405#define SERIAL_ENTRY_TASK_NAME "SerialEntryTask"
406#define TELNET_SHELL_TASK_NAME "TelnetShellTask"
407#define TELNET_ENTRY_TASK_NAME "TelnetEntryTask"
408
409LITE_OS_SEC_TEXT_MINOR UINT32 ShellTaskInit(ShellCB *shellCB)
410{
411    CHAR *name = NULL;
412    TSK_INIT_PARAM_S initParam = {0};
413
414    if (shellCB->consoleID == CONSOLE_SERIAL) {
415        name = SERIAL_SHELL_TASK_NAME;
416    } else if (shellCB->consoleID == CONSOLE_TELNET) {
417        name = TELNET_SHELL_TASK_NAME;
418    } else {
419        return LOS_NOK;
420    }
421
422    initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ShellTask;
423    initParam.usTaskPrio   = 9; /* 9:shell task priority */
424    initParam.auwArgs[0]   = (UINTPTR)shellCB;
425    initParam.uwStackSize  = 0x3000;
426    initParam.pcName       = name;
427    initParam.uwResved     = LOS_TASK_STATUS_DETACHED;
428
429    (VOID)LOS_EventInit(&shellCB->shellEvent);
430
431    return LOS_TaskCreate(&shellCB->shellTaskHandle, &initParam);
432}
433
434LITE_OS_SEC_TEXT_MINOR UINT32 ShellEntryInit(ShellCB *shellCB)
435{
436    UINT32 ret;
437    CHAR *name = NULL;
438    TSK_INIT_PARAM_S initParam = {0};
439
440    if (shellCB->consoleID == CONSOLE_SERIAL) {
441        name = SERIAL_ENTRY_TASK_NAME;
442    } else if (shellCB->consoleID == CONSOLE_TELNET) {
443        name = TELNET_ENTRY_TASK_NAME;
444    } else {
445        return LOS_NOK;
446    }
447
448    initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ShellEntry;
449    initParam.usTaskPrio   = 9; /* 9:shell task priority */
450    initParam.auwArgs[0]   = (UINTPTR)shellCB;
451    initParam.uwStackSize  = 0x1000;
452    initParam.pcName       = name;
453    initParam.uwResved     = LOS_TASK_STATUS_DETACHED;
454
455    ret = LOS_TaskCreate(&shellCB->shellEntryHandle, &initParam);
456#ifdef LOSCFG_PLATFORM_CONSOLE
457    (VOID)ConsoleTaskReg((INT32)shellCB->consoleID, shellCB->shellEntryHandle);
458#endif
459
460    return ret;
461}
462
463