diff --git a/ChangeLog b/ChangeLog index cd2164dc90..c9be40c5d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1485,3 +1485,17 @@ * NSH: 'mem' command renamed to 'free'. Output is now more similar to the Linux 'free' command. + * NSH: Correct a redirection bug in NSH. The following would not work; it + resulted in a hang after the 'cat /dev/fifo': + + nsh> mkfile /dev/fifo + nsh> cd /tmp # /tmp is a mounted RAM disk + nsh> cat /dev/fifo > test.txt & + nsh> echo "This is a test" > /dev/fifo + + The error was caused because (1) there was a path that resulted in stdout + being closed (the "hang") and also (2) the 'cat' command was always outputting + to stdout, not to the redirected file descriptor. Now: + + nsh> cat test.txt + This is a test diff --git a/Documentation/NuttX.html b/Documentation/NuttX.html index 52386107a6..15ee60dae8 100644 --- a/Documentation/NuttX.html +++ b/Documentation/NuttX.html @@ -8,7 +8,7 @@

NuttX RTOS

-

Last Updated: February 27, 2011

+

Last Updated: February 28, 2011

@@ -2087,6 +2087,20 @@ nuttx-5.19 2011-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr> * NSH: 'mem' command renamed to 'free'. Output is now more similar to the Linux 'free' command. + * NSH: Correct a redirection bug in NSH. The following would not work; it + resulted in a hang after the 'cat /dev/fifo': + + nsh> mkfile /dev/fifo + nsh> cd /tmp # /tmp is a mounted RAM disk + nsh> cat /dev/fifo > test.txt & + nsh> echo "This is a test" > /dev/fifo + + The error was caused because (1) there was a path that resulted in stdout + being closed (the "hang") and also (2) the 'cat' command was always outputting + to stdout, not to the redirected file descriptor. Now: + + nsh> cat test.txt + This is a test pascal-2.1 2011-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr> diff --git a/examples/nsh/nsh.h b/examples/nsh/nsh.h index ef03ddc9d5..a7a04a9086 100644 --- a/examples/nsh/nsh.h +++ b/examples/nsh/nsh.h @@ -177,6 +177,7 @@ #define nsh_clone(v) (v)->clone(v) #define nsh_release(v) (v)->release(v) +#define nsh_write(v,b,n) (v)->write(v,b,n) #define nsh_linebuffer(v) (v)->linebuffer(v) #define nsh_redirect(v,f,s) (v)->redirect(v,f,s) #define nsh_undirect(v,s) (v)->undirect(v,s) @@ -255,6 +256,7 @@ struct nsh_vtbl_s void (*addref)(FAR struct nsh_vtbl_s *vtbl); void (*release)(FAR struct nsh_vtbl_s *vtbl); #endif + ssize_t (*write)(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes); int (*output)(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...); FAR char *(*linebuffer)(FAR struct nsh_vtbl_s *vtbl); void (*redirect)(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save); diff --git a/examples/nsh/nsh_fscmds.c b/examples/nsh/nsh_fscmds.c index eb43ed23c6..40f894ec8e 100644 --- a/examples/nsh/nsh_fscmds.c +++ b/examples/nsh/nsh_fscmds.c @@ -457,7 +457,7 @@ int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) while (nbyteswritten < nbytesread) { - int n = write(1, buffer, nbytesread); + ssize_t n = nsh_write(vtbl, buffer, nbytesread); if (n < 0) { /* EINTR is not an error (but will stop stop the cat) */ diff --git a/examples/nsh/nsh_serial.c b/examples/nsh/nsh_serial.c index 1f76de533c..0704cf0800 100644 --- a/examples/nsh/nsh_serial.c +++ b/examples/nsh/nsh_serial.c @@ -1,7 +1,7 @@ /**************************************************************************** * examples/nsh/nsh_serial.c * - * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -45,6 +45,8 @@ #include #include #include +#include +#include #include "nsh.h" @@ -78,6 +80,7 @@ struct serialsave_s static FAR struct nsh_vtbl_s *nsh_consoleclone(FAR struct nsh_vtbl_s *vtbl); static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl); #endif +static ssize_t nsh_consolewrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes); static int nsh_consoleoutput(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...); static FAR char *nsh_consolelinebuffer(FAR struct nsh_vtbl_s *vtbl); static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save); @@ -109,6 +112,7 @@ static inline FAR struct serial_s *nsh_allocstruct(void) pstate->ss_vtbl.clone = nsh_consoleclone; pstate->ss_vtbl.release = nsh_consolerelease; #endif + pstate->ss_vtbl.write = nsh_consolewrite; pstate->ss_vtbl.output = nsh_consoleoutput; pstate->ss_vtbl.linebuffer = nsh_consolelinebuffer; pstate->ss_vtbl.redirect = nsh_consoleredirect; @@ -170,6 +174,41 @@ static void nsh_closeifnotclosed(struct serial_s *pstate) } } +/**************************************************************************** + * Name: nsh_consolewrite + * + * Description: + * write a buffer to the remote shell window. + * + * Currently only used by cat. + * + ****************************************************************************/ + +static ssize_t nsh_consolewrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes) +{ + FAR struct serial_s *pstate = (FAR struct serial_s *)vtbl; + ssize_t 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 (ssize_t)ERROR; + } + + /* Write the data to the output stream */ + + ret = fwrite(buffer, 1, nbytes, pstate->ss_stream); + if (ret < 0) + { + dbg("[%d] Failed to send buffer: %d\n", pstate->ss_fd, errno); + } + return ret; +} + /**************************************************************************** * Name: nsh_consoleoutput * @@ -265,7 +304,29 @@ static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl) * Name: nsh_consoleredirect * * Description: - * Set up for redirected output + * 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_consoleclone, + * - fd:- The file descriptor of the redirected output + * - save: NULL + * + * nsh_consolerelease() 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_consoleundirect() will perform the clean-up after the redirected + * command completes. * ****************************************************************************/ @@ -274,18 +335,39 @@ static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t FAR struct serial_s *pstate = (FAR struct serial_s *)vtbl; FAR struct serialsave_s *ssave = (FAR struct serialsave_s *)save; - (void)nsh_openifnotopen(pstate); - fflush(pstate->ss_stream); + /* Case 1: Redirected foreground commands */ + if (ssave) { + /* pstate->ss_stream and ss_fd refer refer to the + * currently opened output stream. If the console is open, flush + * any pending output. + */ + + if (pstate->ss_stream) + { + fflush(pstate->ss_stream); + } + + /* Save the current fd and stream values. These will be restored + * when nsh_consoleundirect() is called. + */ + ssave->ss_fd = pstate->ss_fd; ssave->ss_stream = pstate->ss_stream; } else { - fclose(pstate->ss_stream); + /* nsh_consoleclone() set pstate->ss_fd and ss_stream to refer + * to standard out. We just want to leave these alone and overwrite + * them with the fd for the re-directed stream. + */ } + /* In either case, set the fd of the new, re-directed output and nullify + * the output stream (it will be fdopen'ed if it is used). + */ + pstate->ss_fd = fd; pstate->ss_stream = NULL; } diff --git a/examples/nsh/nsh_telnetd.c b/examples/nsh/nsh_telnetd.c index 42eb5a9e91..9776124f69 100644 --- a/examples/nsh/nsh_telnetd.c +++ b/examples/nsh/nsh_telnetd.c @@ -1,7 +1,7 @@ /**************************************************************************** * examples/nsh/nsh_telnetd.c * - * Copyright (C) 2007-2010 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2011 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * This is a leverage of similar logic from uIP: @@ -147,6 +147,7 @@ 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); @@ -195,6 +196,7 @@ static FAR struct telnetd_s *nsh_allocstruct(void) 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; @@ -537,6 +539,41 @@ static void *nsh_connection(void *arg) 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 * @@ -704,7 +741,29 @@ static void nsh_telnetrelease(FAR struct nsh_vtbl_s *vtbl) * Name: nsh_telnetredirect * * Description: - * Set up for redirected output + * 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. * ****************************************************************************/