| /**************************************************************************** |
| * apps/nshlib/nsh_telnetd.c |
| * |
| * Copyright (C) 2007-2011 Gregory Nutt. All rights reserved. |
| * Author: Gregory Nutt <spudmonkey@racsa.co.cr> |
| * |
| * This is a leverage of similar logic from uIP: |
| * |
| * Author: Adam Dunkels <adam@sics.se> |
| * Copyright (c) 2003, Adam Dunkels. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the Institute nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <pthread.h> |
| #include <semaphore.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <net/if.h> |
| #include <apps/netutils/uiplib.h> |
| #if defined(CONFIG_NSH_DHCPC) |
| # include <apps/netutils/resolv.h> |
| # include <apps/netutils/dhcpc.h> |
| #endif |
| |
| #include "nsh.h" |
| |
| #ifdef CONFIG_NSH_TELNET |
| |
| /**************************************************************************** |
| * Definitions |
| ****************************************************************************/ |
| |
| #define ISO_nl 0x0a |
| #define ISO_cr 0x0d |
| |
| #define STATE_NORMAL 0 |
| #define STATE_IAC 1 |
| #define STATE_WILL 2 |
| #define STATE_WONT 3 |
| #define STATE_DO 4 |
| #define STATE_DONT 5 |
| #define STATE_CLOSE 6 |
| |
| #define TELNET_IAC 255 |
| #define TELNET_WILL 251 |
| #define TELNET_WONT 252 |
| #define TELNET_DO 253 |
| #define TELNET_DONT 254 |
| |
| #ifdef CONFIG_NSH_TELNETD_DUMPBUFFER |
| # define nsh_telnetdump(vtbl,msg,buf,nb) nsh_dumpbuffer(vtbl,msg,buf,nb) |
| #else |
| # define nsh_telnetdump(vtbl,msg,buf,nb) |
| #endif |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct telnetio_s |
| { |
| sem_t tio_sem; |
| int tio_sockfd; |
| uint8_t tio_bufndx; |
| uint8_t tio_state; |
| char tio_inbuffer[CONFIG_NSH_IOBUFFER_SIZE]; |
| }; |
| |
| struct redirect_s |
| { |
| int rd_fd; /* Re-direct file descriptor */ |
| FILE *rd_stream; /* Re-direct stream */ |
| }; |
| |
| struct telnetsave_s |
| { |
| bool ts_redirected; |
| union |
| { |
| struct telnetio_s *tn; |
| struct redirect_s rd; |
| } u; |
| }; |
| |
| struct telnetd_s |
| { |
| struct nsh_vtbl_s tn_vtbl; |
| uint16_t tn_sndlen; |
| bool tn_redirected; |
| union |
| { |
| struct telnetio_s *tn; |
| struct redirect_s rd; |
| } u; |
| char tn_outbuffer[CONFIG_NSH_IOBUFFER_SIZE]; |
| char tn_cmd[CONFIG_NSH_LINELEN]; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_NSH_DISABLEBG |
| static void tio_semtake(struct telnetio_s *tio); |
| static FAR struct nsh_vtbl_s *nsh_telnetclone(FAR struct nsh_vtbl_s *vtbl); |
| #endif |
| static void nsh_telnetrelease(FAR struct nsh_vtbl_s *vtbl); |
| static ssize_t nsh_telnetwrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes); |
| static int nsh_telnetoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...); |
| static int nsh_redirectoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...); |
| static FAR char *nsh_telnetlinebuffer(FAR struct nsh_vtbl_s *vtbl); |
| static void nsh_telnetredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save); |
| static void nsh_telnetundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save); |
| static void nsh_telnetexit(FAR struct nsh_vtbl_s *vtbl); |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: tio_semtake |
| ****************************************************************************/ |
| |
| static void tio_semtake(struct telnetio_s *tio) |
| { |
| /* Take the semaphore (perhaps waiting) */ |
| |
| while (sem_wait(&tio->tio_sem) != 0) |
| { |
| /* The only case that an error should occur here is if the wait was |
| * awakened by a signal. |
| */ |
| |
| ASSERT(errno == EINTR); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: tio_semgive |
| ****************************************************************************/ |
| |
| #define tio_semgive(tio) ASSERT(sem_post(&tio->tio_sem) == 0) |
| |
| /**************************************************************************** |
| * Name: nsh_allocstruct |
| ****************************************************************************/ |
| |
| static FAR struct telnetd_s *nsh_allocstruct(void) |
| { |
| struct telnetd_s *pstate = (struct telnetd_s *)zalloc(sizeof(struct telnetd_s)); |
| if (pstate) |
| { |
| #ifndef CONFIG_NSH_DISABLEBG |
| pstate->tn_vtbl.clone = nsh_telnetclone; |
| pstate->tn_vtbl.release = nsh_telnetrelease; |
| #endif |
| pstate->tn_vtbl.write = nsh_telnetwrite; |
| pstate->tn_vtbl.output = nsh_telnetoutput; |
| pstate->tn_vtbl.linebuffer = nsh_telnetlinebuffer; |
| pstate->tn_vtbl.redirect = nsh_telnetredirect; |
| pstate->tn_vtbl.undirect = nsh_telnetundirect; |
| pstate->tn_vtbl.exit = nsh_telnetexit; |
| } |
| return pstate; |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_openifnotopen |
| ****************************************************************************/ |
| |
| static int nsh_openifnotopen(struct telnetd_s *pstate) |
| { |
| struct redirect_s *rd = &pstate->u.rd; |
| |
| /* The stream is open in a lazy fashion. This is done because the file |
| * descriptor may be opened on a different task than the stream. |
| */ |
| |
| if (!rd->rd_stream) |
| { |
| rd->rd_stream = fdopen(rd->rd_fd, "w"); |
| if (!rd->rd_stream) |
| { |
| return ERROR; |
| } |
| } |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_closeifnotclosed |
| ****************************************************************************/ |
| |
| static void nsh_closeifnotclosed(struct telnetd_s *pstate) |
| { |
| struct redirect_s *rd = &pstate->u.rd; |
| |
| if (rd->rd_stream == stdout) |
| { |
| fflush(stdout); |
| rd->rd_fd = 1; |
| } |
| else |
| { |
| if (rd->rd_stream) |
| { |
| fflush(rd->rd_stream); |
| fclose(rd->rd_stream); |
| } |
| else if (rd->rd_fd >= 0 && rd->rd_fd != 1) |
| { |
| close(rd->rd_fd); |
| } |
| |
| rd->rd_fd = -1; |
| rd->rd_stream = NULL; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_putchar |
| * |
| * Description: |
| * Add another parsed character to the TELNET command string |
| * |
| * Assumption: |
| * Caller holds TIO semaphore |
| * |
| ****************************************************************************/ |
| |
| static void nsh_putchar(struct telnetd_s *pstate, uint8_t ch) |
| { |
| struct telnetio_s *tio = pstate->u.tn; |
| |
| /* Ignore carriage returns */ |
| |
| if (ch == ISO_cr) |
| { |
| return; |
| } |
| |
| /* Add all other characters to the cmd buffer */ |
| |
| pstate->tn_cmd[tio->tio_bufndx] = ch; |
| |
| /* If a newline was added or if the buffer is full, then process it now */ |
| |
| if (ch == ISO_nl || tio->tio_bufndx == (CONFIG_NSH_LINELEN - 1)) |
| { |
| pstate->tn_cmd[tio->tio_bufndx] = '\0'; |
| nsh_telnetdump(&pstate->tn_vtbl, "TELNET CMD", |
| (uint8_t*)pstate->tn_cmd, strlen(pstate->tn_cmd)); |
| nsh_parse(&pstate->tn_vtbl, pstate->tn_cmd); |
| tio->tio_bufndx = 0; |
| } |
| else |
| { |
| tio->tio_bufndx++; |
| vdbg("Add '%c', bufndx=%d\n", ch, tio->tio_bufndx); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_sendopt |
| * |
| * Description: |
| * |
| ****************************************************************************/ |
| |
| static void nsh_sendopt(struct telnetd_s *pstate, uint8_t option, uint8_t value) |
| { |
| struct telnetio_s *tio = pstate->u.tn; |
| uint8_t optbuf[4]; |
| optbuf[0] = TELNET_IAC; |
| optbuf[1] = option; |
| optbuf[2] = value; |
| optbuf[3] = 0; |
| |
| nsh_telnetdump(&pstate->tn_vtbl, "Send optbuf", optbuf, 4); |
| tio_semtake(tio); /* Only one call to send at a time */ |
| if (send(tio->tio_sockfd, optbuf, 4, 0) < 0) |
| { |
| dbg("[%d] Failed to send TELNET_IAC: %d\n", tio->tio_sockfd, errno); |
| } |
| tio_semgive(tio); |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_flush |
| * |
| * Description: |
| * Dump the buffered output info. |
| * |
| ****************************************************************************/ |
| |
| static void nsh_flush(FAR struct telnetd_s *pstate) |
| { |
| struct telnetio_s *tio = pstate->u.tn; |
| |
| if (pstate->tn_sndlen > 0) |
| { |
| nsh_telnetdump(&pstate->tn_vtbl, "Shell output", |
| (uint8_t*)pstate->tn_outbuffer, pstate->tn_sndlen); |
| tio_semtake(tio); /* Only one call to send at a time */ |
| if (send(tio->tio_sockfd, pstate->tn_outbuffer, pstate->tn_sndlen, 0) < 0) |
| { |
| dbg("[%d] Failed to send response: %d\n", tio->tio_sockfd, errno); |
| } |
| tio_semgive(tio); |
| } |
| pstate->tn_sndlen = 0; |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_receive |
| * |
| * Description: |
| * Process a received TELENET buffer |
| * |
| ****************************************************************************/ |
| |
| static int nsh_receive(struct telnetd_s *pstate, size_t len) |
| { |
| struct telnetio_s *tio = pstate->u.tn; |
| char *ptr = tio->tio_inbuffer; |
| uint8_t ch; |
| |
| while (len > 0) |
| { |
| ch = *ptr++; |
| len--; |
| |
| vdbg("ch=%02x state=%d\n", ch, tio->tio_state); |
| switch (tio->tio_state) |
| { |
| case STATE_IAC: |
| if (ch == TELNET_IAC) |
| { |
| nsh_putchar(pstate, ch); |
| tio->tio_state = STATE_NORMAL; |
| } |
| else |
| { |
| switch (ch) |
| { |
| case TELNET_WILL: |
| tio->tio_state = STATE_WILL; |
| break; |
| |
| case TELNET_WONT: |
| tio->tio_state = STATE_WONT; |
| break; |
| |
| case TELNET_DO: |
| tio->tio_state = STATE_DO; |
| break; |
| |
| case TELNET_DONT: |
| tio->tio_state = STATE_DONT; |
| break; |
| |
| default: |
| tio->tio_state = STATE_NORMAL; |
| break; |
| } |
| } |
| break; |
| |
| case STATE_WILL: |
| /* Reply with a DONT */ |
| |
| nsh_sendopt(pstate, TELNET_DONT, ch); |
| tio->tio_state = STATE_NORMAL; |
| break; |
| |
| case STATE_WONT: |
| /* Reply with a DONT */ |
| |
| nsh_sendopt(pstate, TELNET_DONT, ch); |
| tio->tio_state = STATE_NORMAL; |
| break; |
| |
| case STATE_DO: |
| /* Reply with a WONT */ |
| |
| nsh_sendopt(pstate, TELNET_WONT, ch); |
| tio->tio_state = STATE_NORMAL; |
| break; |
| |
| case STATE_DONT: |
| /* Reply with a WONT */ |
| |
| nsh_sendopt(pstate, TELNET_WONT, ch); |
| tio->tio_state = STATE_NORMAL; |
| break; |
| |
| case STATE_NORMAL: |
| if (ch == TELNET_IAC) |
| { |
| tio->tio_state = STATE_IAC; |
| } |
| else |
| { |
| nsh_putchar(pstate, ch); |
| } |
| break; |
| } |
| } |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_connection |
| * |
| * Description: |
| * Each time a new connection to port 23 is made, a new thread is created |
| * that begins at this entry point. There should be exactly one argument |
| * and it should be the socket descriptor (+1). |
| * |
| ****************************************************************************/ |
| |
| static void *nsh_connection(void *arg) |
| { |
| struct telnetd_s *pstate = nsh_allocstruct(); |
| struct telnetio_s *tio = (struct telnetio_s *)zalloc(sizeof(struct telnetio_s)); |
| struct nsh_vtbl_s *vtbl = &pstate->tn_vtbl; |
| int sockfd = (int)arg; |
| int ret = ERROR; |
| |
| dbg("[%d] Started\n", sockfd); |
| |
| /* Verify that the state structure was successfully allocated */ |
| |
| if (pstate && tio) |
| { |
| /* Initialize the thread state structure */ |
| |
| sem_init(&tio->tio_sem, 0, 1); |
| tio->tio_sockfd = sockfd; |
| tio->tio_state = STATE_NORMAL; |
| pstate->u.tn = tio; |
| |
| /* Output a greeting */ |
| |
| nsh_output(vtbl, g_nshgreeting); |
| |
| /* Execute the startup script */ |
| |
| #if defined(CONFIG_NSH_ROMFSETC) && !defined(CONFIG_NSH_CONSOLE) |
| (void)nsh_script(vtbl, "init", NSH_INITPATH); |
| #endif |
| |
| /* Loop processing each TELNET command */ |
| |
| do |
| { |
| /* Display the prompt string */ |
| |
| nsh_output(vtbl, g_nshprompt); |
| nsh_flush(pstate); |
| |
| /* Read a buffer of data from the TELNET client */ |
| |
| ret = recv(tio->tio_sockfd, tio->tio_inbuffer, |
| CONFIG_NSH_IOBUFFER_SIZE, 0); |
| if (ret > 0) |
| { |
| |
| /* Process the received TELNET data */ |
| |
| nsh_telnetdump(vtbl, "Received buffer", |
| (uint8_t*)tio->tio_inbuffer, ret); |
| ret = nsh_receive(pstate, ret); |
| } |
| } |
| while (ret >= 0 && tio->tio_state != STATE_CLOSE); |
| dbg("[%d] ret=%d tn.tio_state=%d\n", sockfd, ret, tio->tio_state); |
| |
| /* End of command processing -- Clean up and exit */ |
| } |
| |
| /* Exit the task */ |
| |
| if (pstate) |
| { |
| free(pstate); |
| } |
| |
| if (tio) |
| { |
| sem_destroy(&tio->tio_sem); |
| free(tio); |
| } |
| |
| dbg("[%d] Exitting\n", sockfd); |
| close(sockfd); |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_telnetwrite |
| * |
| * Description: |
| * write a buffer to the remote shell window. |
| * |
| * Currently only used by cat. |
| * |
| ****************************************************************************/ |
| |
| static ssize_t nsh_telnetwrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes) |
| { |
| struct telnetd_s *pstate = (struct telnetd_s *)vtbl; |
| struct telnetio_s *tio = pstate->u.tn; |
| ssize_t ret = nbytes; |
| |
| /* Flush anything already in the output buffer */ |
| |
| nsh_flush(pstate); |
| |
| /* Then write the user buffer */ |
| |
| nsh_telnetdump(&pstate->tn_vtbl, "Buffer output",(uint8_t*)buffer, nbytes); |
| |
| tio_semtake(tio); /* Only one call to send at a time */ |
| ret = send(tio->tio_sockfd, buffer, nbytes, 0); |
| if (ret < 0) |
| { |
| dbg("[%d] Failed to send buffer: %d\n", tio->tio_sockfd, errno); |
| } |
| |
| tio_semgive(tio); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_telnetoutput |
| * |
| * Description: |
| * Print a string to the remote shell window. |
| * |
| * This function is implemented by the shell GUI / telnet server and |
| * can be called by the shell back-end to output a string in the |
| * shell window. The string is automatically appended with a linebreak. |
| * |
| ****************************************************************************/ |
| |
| static int nsh_telnetoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...) |
| { |
| struct telnetd_s *pstate = (struct telnetd_s *)vtbl; |
| int nbytes = pstate->tn_sndlen; |
| int len; |
| va_list ap; |
| |
| /* Put the new info into the buffer. Here we are counting on the fact that |
| * no output strings will exceed CONFIG_NSH_LINELEN! |
| */ |
| |
| va_start(ap, fmt); |
| vsnprintf(&pstate->tn_outbuffer[nbytes], |
| (CONFIG_NSH_IOBUFFER_SIZE - 1) - nbytes, fmt, ap); |
| va_end(ap); |
| |
| /* Get the size of the new string just added and the total size of |
| * buffered data |
| */ |
| |
| len = strlen(&pstate->tn_outbuffer[nbytes]); |
| nbytes += len; |
| |
| /* Expand any terminating \n to \r\n */ |
| |
| if (nbytes < (CONFIG_NSH_IOBUFFER_SIZE - 2) && |
| pstate->tn_outbuffer[nbytes-1] == '\n') |
| { |
| pstate->tn_outbuffer[nbytes-1] = ISO_cr; |
| pstate->tn_outbuffer[nbytes] = ISO_nl; |
| pstate->tn_outbuffer[nbytes+1] = '\0'; |
| nbytes++; |
| } |
| pstate->tn_sndlen = nbytes; |
| |
| /* Flush to the network if the buffer does not have room for one more |
| * maximum length string. |
| */ |
| |
| if (nbytes > CONFIG_NSH_IOBUFFER_SIZE - CONFIG_NSH_LINELEN) |
| { |
| nsh_flush(pstate); |
| } |
| |
| return len; |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_redirectoutput |
| * |
| * Description: |
| * Print a string to the currently selected stream. |
| * |
| ****************************************************************************/ |
| |
| static int nsh_redirectoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...) |
| { |
| FAR struct telnetd_s *pstate = (FAR struct telnetd_s *)vtbl; |
| va_list ap; |
| int ret; |
| |
| /* The stream is open in a lazy fashion. This is done because the file |
| * descriptor may be opened on a different task than the stream. The |
| * actual open will then occur with the first output from the new task. |
| */ |
| |
| if (nsh_openifnotopen(pstate) != 0) |
| { |
| return ERROR; |
| } |
| |
| va_start(ap, fmt); |
| ret = vfprintf(pstate->u.rd.rd_stream, fmt, ap); |
| va_end(ap); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_telnetlinebuffer |
| * |
| * Description: |
| * Return a reference to the current line buffer |
| * |
| * ****************************************************************************/ |
| |
| static FAR char *nsh_telnetlinebuffer(FAR struct nsh_vtbl_s *vtbl) |
| { |
| struct telnetd_s *pstate = (struct telnetd_s *)vtbl; |
| return pstate->tn_cmd; |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_telnetclone |
| * |
| * Description: |
| * Make an independent copy of the vtbl |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_NSH_DISABLEBG |
| static FAR struct nsh_vtbl_s *nsh_telnetclone(FAR struct nsh_vtbl_s *vtbl) |
| { |
| FAR struct telnetd_s *pstate = (FAR struct telnetd_s *)vtbl; |
| FAR struct telnetd_s *pclone = nsh_allocstruct(); |
| FAR struct nsh_vtbl_s *ret = NULL; |
| |
| if (pclone) |
| { |
| if (pstate->tn_redirected) |
| { |
| pclone->tn_redirected = true; |
| pclone->tn_vtbl.output = nsh_redirectoutput; |
| pclone->u.rd.rd_fd = pstate->u.rd.rd_fd; |
| pclone->u.rd.rd_stream = NULL; |
| } |
| else |
| { |
| pclone->u.tn = pstate->u.tn; |
| } |
| ret = &pclone->tn_vtbl; |
| } |
| return ret; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: nsh_telnetrelease |
| * |
| * Description: |
| * Release the cloned instance |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_NSH_DISABLEBG |
| static void nsh_telnetrelease(FAR struct nsh_vtbl_s *vtbl) |
| { |
| FAR struct telnetd_s *pstate = (FAR struct telnetd_s *)vtbl; |
| |
| if (pstate->tn_redirected) |
| { |
| nsh_closeifnotclosed(pstate); |
| } |
| else |
| { |
| nsh_flush(pstate); |
| } |
| free(pstate); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: nsh_telnetredirect |
| * |
| * Description: |
| * Set up for redirected output. This function is called from nsh_parse() |
| * in two different contexts: |
| * |
| * 1) Redirected background commands of the form: command > xyz.text & |
| * |
| * In this case: |
| * - vtbl: A newly allocated and initialized instance created by |
| * nsh_telnetclone, |
| * - fd:- The file descriptor of the redirected output |
| * - save: NULL |
| * |
| * nsh_telnetrelease() will perform the clean-up when the clone is |
| * destroyed. |
| * |
| * 2) Redirected foreground commands of the form: command > xyz.txt |
| * |
| * In this case: |
| * - vtbl: The current state structure, |
| * - fd: The file descriptor of the redirected output |
| * - save: Where to save the re-directed registers. |
| * |
| * nsh_telnetundirect() will perform the clean-up after the redirected |
| * command completes. |
| * |
| ****************************************************************************/ |
| |
| static void nsh_telnetredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save) |
| { |
| FAR struct telnetd_s *pstate = (FAR struct telnetd_s *)vtbl; |
| FAR struct telnetsave_s *ssave = (FAR struct telnetsave_s *)save; |
| |
| if (pstate->tn_redirected) |
| { |
| (void)nsh_openifnotopen(pstate); |
| fflush(pstate->u.rd.rd_stream); |
| if (!ssave) |
| { |
| fclose(pstate->u.rd.rd_stream); |
| } |
| } |
| |
| if (ssave) |
| { |
| ssave->ts_redirected = pstate->tn_redirected; |
| memcpy(&ssave->u.rd, &pstate->u.rd, sizeof(struct redirect_s)); |
| } |
| |
| pstate->tn_redirected = true; |
| pstate->u.rd.rd_fd = fd; |
| pstate->u.rd.rd_stream = NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_telnetundirect |
| * |
| * Description: |
| * Set up for redirected output |
| * |
| ****************************************************************************/ |
| |
| static void nsh_telnetundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save) |
| { |
| FAR struct telnetd_s *pstate = (FAR struct telnetd_s *)vtbl; |
| FAR struct telnetsave_s *ssave = (FAR struct telnetsave_s *)save; |
| |
| if (pstate->tn_redirected) |
| { |
| nsh_closeifnotclosed(pstate); |
| } |
| |
| pstate->tn_redirected = ssave->ts_redirected; |
| memcpy(&pstate->u.rd, &ssave->u.rd, sizeof(struct redirect_s)); |
| } |
| |
| /**************************************************************************** |
| * Name: nsh_telnetexit |
| * |
| * Description: |
| * Quit the shell instance |
| * |
| ****************************************************************************/ |
| |
| static void nsh_telnetexit(FAR struct nsh_vtbl_s *vtbl) |
| { |
| struct telnetd_s *pstate = (struct telnetd_s *)vtbl; |
| pstate->u.tn->tio_state = STATE_CLOSE; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nsh_telnetmain |
| * |
| * Description: |
| * This is the main processing thread for telnetd. It never returns |
| * unless an error occurs |
| * |
| ****************************************************************************/ |
| |
| int nsh_telnetmain(int argc, char *argv[]) |
| { |
| /* Execute nsh_connection() on each connection to port 23 */ |
| |
| uip_server(HTONS(23), nsh_connection, CONFIG_NSH_STACKSIZE); |
| return OK; |
| } |
| |
| #endif /* CONFIG_NSH_TELNET */ |