Skip to content

Commit

Permalink
Special handling for TCP clients in the shell
Browse files Browse the repository at this point in the history
  • Loading branch information
rweather committed Mar 11, 2016
1 parent 6f03fa2 commit 9221104
Showing 5 changed files with 80 additions and 9 deletions.
65 changes: 64 additions & 1 deletion libraries/Terminal/Shell.cpp
Original file line number Diff line number Diff line change
@@ -133,6 +133,7 @@ Shell::Shell()
, historyPosn(0)
, prom("> ")
, hideChars(false)
, isClient(false)
{
}

@@ -152,7 +153,7 @@ Shell::~Shell()
* \param maxHistory The number of commands to allocate in the history
* stack for scrolling back through using Up/Down arrow keys.
* \param mode The terminal mode to operate in, Terminal::Serial or
* Terminal::Telnet.
* Terminal::Telnet. Default is Terminal::Serial.
* \return Returns true if the shell was initialized, or false if there
* is insufficient memory for the history stack.
*
@@ -169,6 +170,44 @@ Shell::~Shell()
* \sa end(), setPrompt()
*/
bool Shell::begin(Stream &stream, size_t maxHistory, Terminal::Mode mode)
{
if (!beginShell(stream, maxHistory, mode))
return false;
isClient = false;
return true;
}

/**
* \brief Begin shell handling on a connected TCP client.
*
* \param client The client to apply the shell to. This must be a
* connected TCP client.
* \param maxHistory The number of commands to allocate in the history
* stack for scrolling back through using Up/Down arrow keys.
* \param mode The terminal mode to operate in, Terminal::Serial or
* Terminal::Telnet. Default is Terminal::Telnet.
* \return Returns true if the shell was initialized, or false if there
* is insufficient memory for the history stack.
*
* This override is provided as a convenience for starting a shell on a
* TCP connection. This function also modifies the behaviour of the
* builtin "exit" command to forcibly stop the TCP connection rather
* than returning to the login prompt.
*
* \sa end(), setPrompt()
*/
bool Shell::begin(Client &client, size_t maxHistory, Terminal::Mode mode)
{
if (!beginShell(client, maxHistory, mode))
return false;
isClient = true;
return true;
}

/**
* \brief Internal implementation of begin().
*/
bool Shell::beginShell(Stream &stream, size_t maxHistory, Terminal::Mode mode)
{
// Initialize the Terminal base class with the underlying stream.
Terminal::begin(stream, mode);
@@ -217,6 +256,7 @@ void Shell::end()
historyWrite = 0;
historyPosn = 0;
hideChars = false;
isClient = false;
}

/** @cond */
@@ -236,6 +276,12 @@ static char const builtin_cmd_help_alt[] PROGMEM = "?";
*/
void Shell::loop()
{
// If the stream is a TCP client, then check for disconnection.
if (isClient && !((Client *)stream())->connected()) {
end();
return;
}

// Read the next key and bail out if none. We only process a single
// key each time we enter this function to prevent other tasks in the
// system from becoming starved of time resources if the bytes are
@@ -499,6 +545,21 @@ void Shell::help()
}
}

/**
* \brief Exit from the shell back to the login prompt.
*
* If the underlying stream is a TCP client, then this function will
* stop the client, causing disconnection.
*/
void Shell::exit()
{
Stream *stream = this->stream();
if (isClient) {
end();
((Client *)stream)->stop();
}
}

/**
* \brief Executes the command in the buffer.
*/
@@ -538,6 +599,8 @@ void Shell::execute()
if (!strcmp_P(argv0, builtin_cmd_help) ||
!strcmp_P(argv0, builtin_cmd_help_alt)) {
help();
} else if (!strcmp_P(argv0, builtin_cmd_exit)) {
exit();
} else {
static char const unknown_cmd[] PROGMEM = "Unknown command: ";
writeProgMem(unknown_cmd);
5 changes: 5 additions & 0 deletions libraries/Terminal/Shell.h
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@
#define SHELL_h

#include "Terminal.h"
#include <Client.h>

class Shell;
class ShellArguments;
@@ -65,6 +66,7 @@ class Shell : public Terminal
virtual ~Shell();

bool begin(Stream &stream, size_t maxHistory = 0, Terminal::Mode mode = Serial);
bool begin(Client &client, size_t maxHistory = 0, Terminal::Mode mode = Telnet);
void end();

void loop();
@@ -78,6 +80,7 @@ class Shell : public Terminal
void setHideCharacters(bool hide);

void help();
void exit();

private:
char buffer[SHELL_MAX_CMD_LEN];
@@ -88,11 +91,13 @@ class Shell : public Terminal
size_t historyPosn;
const char *prom;
bool hideChars;
bool isClient;

// Disable copy constructor and operator=().
Shell(const Shell &other) {}
Shell &operator=(const Shell &) { return *this; }

bool beginShell(Stream &stream, size_t maxHistory, Terminal::Mode mode);
void execute();
bool execute(const ShellArguments &argv);
void executeBuiltin(const char *cmd);
10 changes: 9 additions & 1 deletion libraries/Terminal/Terminal.cpp
Original file line number Diff line number Diff line change
@@ -178,7 +178,7 @@ Terminal::~Terminal()
* will be interpreted. This is useful if the underlying \a stream is a TCP
* connection on port 23. The mode operates as a telnet server.
*
* \sa end(), mode()
* \sa end(), stream(), mode()
*/
void Terminal::begin(Stream &stream, Mode mode)
{
@@ -200,6 +200,14 @@ void Terminal::end()
_stream = 0;
}

/**
* \fn Stream *Terminal::stream() const
* \brief Returns a pointer to the underlying Stream, or NULL if the
* stream has not been set with begin() yet.
*
* \sa begin()
*/

/**
* \fn Terminal::Mode Terminal::mode() const
* \brief Returns the mode this terminal is operating in, Serial or Telnet.
1 change: 1 addition & 0 deletions libraries/Terminal/Terminal.h
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@ class Terminal : public Stream
void begin(Stream &stream, Mode mode = Serial);
void end();

Stream *stream() const { return _stream; }
Terminal::Mode mode() const { return (Terminal::Mode)mod; }

virtual int available();
8 changes: 1 addition & 7 deletions libraries/Terminal/examples/TelnetServer/TelnetServer.ino
Original file line number Diff line number Diff line change
@@ -29,13 +29,7 @@ void cmdLed(Shell &shell, int argc, const ShellArguments &argv)
digitalWrite(ledPin, LOW);
}

void cmdExit(Shell &shell, int argc, const ShellArguments &argv)
{
client.stop();
}

ShellCommand(led, "Turns the status LED on or off", cmdLed);
ShellCommand(exit, "Exit and log out", cmdExit);

void setup()
{
@@ -72,7 +66,7 @@ void loop()
client = server.available();
if (client) {
haveClient = true;
shell.begin(client, 5, Terminal::Telnet);
shell.begin(client, 5);
}
} else if (!client.connected()) {
// The current client has been disconnected. Shut down the shell.

0 comments on commit 9221104

Please sign in to comment.