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)
, 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);

View File

@ -24,6 +24,7 @@
#define SHELL_h
#include "Terminal.h"
#include <Client.h>
class Shell;
class ShellArguments;
@ -65,6 +66,7 @@ public:
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 @@ public:
void setHideCharacters(bool hide);
void help();
void exit();
private:
char buffer[SHELL_MAX_CMD_LEN];
@ -88,11 +91,13 @@ private:
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);

View File

@ -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.

View File

@ -48,6 +48,7 @@ public:
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();

View File

@ -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.