1
0
mirror of https://github.com/taigrr/arduinolibs synced 2025-01-18 04:33:12 -08:00

Special handling for TCP clients in the shell

This commit is contained in:
Rhys Weatherley 2016-03-12 05:51:33 +10:00
parent 6f03fa2cfc
commit 9221104977
5 changed files with 80 additions and 9 deletions

View File

@ -133,6 +133,7 @@ Shell::Shell()
, historyPosn(0) , historyPosn(0)
, prom("> ") , prom("> ")
, hideChars(false) , hideChars(false)
, isClient(false)
{ {
} }
@ -152,7 +153,7 @@ Shell::~Shell()
* \param maxHistory The number of commands to allocate in the history * \param maxHistory The number of commands to allocate in the history
* stack for scrolling back through using Up/Down arrow keys. * stack for scrolling back through using Up/Down arrow keys.
* \param mode The terminal mode to operate in, Terminal::Serial or * \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 * \return Returns true if the shell was initialized, or false if there
* is insufficient memory for the history stack. * is insufficient memory for the history stack.
* *
@ -169,6 +170,44 @@ Shell::~Shell()
* \sa end(), setPrompt() * \sa end(), setPrompt()
*/ */
bool Shell::begin(Stream &stream, size_t maxHistory, Terminal::Mode mode) 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. // Initialize the Terminal base class with the underlying stream.
Terminal::begin(stream, mode); Terminal::begin(stream, mode);
@ -217,6 +256,7 @@ void Shell::end()
historyWrite = 0; historyWrite = 0;
historyPosn = 0; historyPosn = 0;
hideChars = false; hideChars = false;
isClient = false;
} }
/** @cond */ /** @cond */
@ -236,6 +276,12 @@ static char const builtin_cmd_help_alt[] PROGMEM = "?";
*/ */
void Shell::loop() 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 // 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 // key each time we enter this function to prevent other tasks in the
// system from becoming starved of time resources if the bytes are // 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. * \brief Executes the command in the buffer.
*/ */
@ -538,6 +599,8 @@ void Shell::execute()
if (!strcmp_P(argv0, builtin_cmd_help) || if (!strcmp_P(argv0, builtin_cmd_help) ||
!strcmp_P(argv0, builtin_cmd_help_alt)) { !strcmp_P(argv0, builtin_cmd_help_alt)) {
help(); help();
} else if (!strcmp_P(argv0, builtin_cmd_exit)) {
exit();
} else { } else {
static char const unknown_cmd[] PROGMEM = "Unknown command: "; static char const unknown_cmd[] PROGMEM = "Unknown command: ";
writeProgMem(unknown_cmd); writeProgMem(unknown_cmd);

View File

@ -24,6 +24,7 @@
#define SHELL_h #define SHELL_h
#include "Terminal.h" #include "Terminal.h"
#include <Client.h>
class Shell; class Shell;
class ShellArguments; class ShellArguments;
@ -65,6 +66,7 @@ public:
virtual ~Shell(); virtual ~Shell();
bool begin(Stream &stream, size_t maxHistory = 0, Terminal::Mode mode = Serial); 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 end();
void loop(); void loop();
@ -78,6 +80,7 @@ public:
void setHideCharacters(bool hide); void setHideCharacters(bool hide);
void help(); void help();
void exit();
private: private:
char buffer[SHELL_MAX_CMD_LEN]; char buffer[SHELL_MAX_CMD_LEN];
@ -88,11 +91,13 @@ private:
size_t historyPosn; size_t historyPosn;
const char *prom; const char *prom;
bool hideChars; bool hideChars;
bool isClient;
// Disable copy constructor and operator=(). // Disable copy constructor and operator=().
Shell(const Shell &other) {} Shell(const Shell &other) {}
Shell &operator=(const Shell &) { return *this; } Shell &operator=(const Shell &) { return *this; }
bool beginShell(Stream &stream, size_t maxHistory, Terminal::Mode mode);
void execute(); void execute();
bool execute(const ShellArguments &argv); bool execute(const ShellArguments &argv);
void executeBuiltin(const char *cmd); void executeBuiltin(const char *cmd);

View File

@ -178,7 +178,7 @@ Terminal::~Terminal()
* will be interpreted. This is useful if the underlying \a stream is a TCP * 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. * 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) void Terminal::begin(Stream &stream, Mode mode)
{ {
@ -200,6 +200,14 @@ void Terminal::end()
_stream = 0; _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 * \fn Terminal::Mode Terminal::mode() const
* \brief Returns the mode this terminal is operating in, Serial or Telnet. * \brief Returns the mode this terminal is operating in, Serial or Telnet.

View File

@ -48,6 +48,7 @@ public:
void begin(Stream &stream, Mode mode = Serial); void begin(Stream &stream, Mode mode = Serial);
void end(); void end();
Stream *stream() const { return _stream; }
Terminal::Mode mode() const { return (Terminal::Mode)mod; } Terminal::Mode mode() const { return (Terminal::Mode)mod; }
virtual int available(); virtual int available();

View File

@ -29,13 +29,7 @@ void cmdLed(Shell &shell, int argc, const ShellArguments &argv)
digitalWrite(ledPin, LOW); 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(led, "Turns the status LED on or off", cmdLed);
ShellCommand(exit, "Exit and log out", cmdExit);
void setup() void setup()
{ {
@ -72,7 +66,7 @@ void loop()
client = server.available(); client = server.available();
if (client) { if (client) {
haveClient = true; haveClient = true;
shell.begin(client, 5, Terminal::Telnet); shell.begin(client, 5);
} }
} else if (!client.connected()) { } else if (!client.connected()) {
// The current client has been disconnected. Shut down the shell. // The current client has been disconnected. Shut down the shell.