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

Login support for the shell

This commit is contained in:
Rhys Weatherley 2016-03-12 10:51:17 +10:00
parent 9221104977
commit ab914c4d07
5 changed files with 418 additions and 102 deletions

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "LoginShell.h"
/**
* \class LoginShell LoginShell.h <LoginShell.h>
* \brief Command-line shell access via a login shell.
*
* This class provides a command-line shell with login support.
* The user is prompted for username and password when they connect
* and other commands will not be available until the correct credentials
* have been supplied.
*
* \sa Shell
*/
/**
* \typedef ShellPasswordCheckFunc
* \brief Password checking function for login shells.
*
* \param username Points to the user name that was supplied at login.
* \param password Points to the password that was supplied at login.
*
* \return Returns zero or greater if the username and password combination
* is correct, negative if incorrect.
*
* The return value is reported to the application as LoginShell::userid(),
* which can be used by the application to restrict the set of commands
* that are available to the user, or to restrict the behaviour of
* those commands when acting on critical resources.
*
* Timing can be very important: the check should take the same amount of
* time for valid and invalid user identifiers or passwords so that an
* attacker cannot gain knowledge about the valid users on the system
* based on failed login attempts.
*
* \relates LoginShell
* \sa LoginShell::userid()
*/
/**
* \brief Constructs a new login shell.
*
* This constructor must be followed by a call to begin() to specify
* the underlying I/O stream.
*/
LoginShell::LoginShell()
: machName(0)
, checkFunc(0)
, uid(-1)
{
}
/**
* \brief Destroys this login shell.
*/
LoginShell::~LoginShell()
{
}
/**
* \fn int LoginShell::userid() const
* \brief Gets the user identifier for the currently logged in user,
* or -1 if there is no user logged in currently.
*
* The user identifier can be used by applications to restrict the set of
* commands that are available to the user, or to restrict the behaviour
* of those commands when acting on critical resources.
*
* \sa ShellPasswordCheckFunc
*/

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef LOGIN_SHELL_h
#define LOGIN_SHELL_h
#include "Shell.h"
typedef int (*ShellPasswordCheckFunc)(const char *username, const char *password);
class LoginShell : public Shell
{
public:
LoginShell();
virtual ~LoginShell();
const char *machineName() const { return machName; }
void setMachineName(const char *machineName) { machName = machineName; }
ShellPasswordCheckFunc passwordCheckFunction() const { return checkFunc; }
void setPasswordCheckFunction(ShellPasswordCheckFunc function) { checkFunc = function; }
int userid() const { return uid; }
protected:
virtual void beginSession();
virtual void printPrompt();
virtual void execute();
private:
const char *machName;
ShellPasswordCheckFunc checkFunc;
int uid;
};
#endif

View File

@ -21,6 +21,7 @@
*/ */
#include "Shell.h" #include "Shell.h"
#include "LoginShell.h"
#include <string.h> #include <string.h>
#include <stddef.h> #include <stddef.h>
@ -79,7 +80,7 @@
* shell.begin(Serial, 5); * shell.begin(Serial, 5);
* \endcode * \endcode
* *
* \sa Terminal * \sa LoginShell, Terminal
*/ */
/** /**
@ -101,23 +102,16 @@
* \relates Shell * \relates Shell
*/ */
/** // Modes for line editing (flags).
* \typedef ShellPasswordCheckFunc #define LINEMODE_NORMAL 0x01
* \brief Password checking function for login shells. #define LINEMODE_ECHO 0x02
* #define LINEMODE_USERNAME 0x04
* \param userid Points to the user identifier that was supplied at login. #define LINEMODE_PASSWORD 0x08
* \param password Points to the password that was supplied at login. #define LINEMODE_PROMPT 0x10
* #define LINEMODE_DELAY 0x20
* \return Returns true if the user identifier and password combination
* is correct, false if incorrect. // Delay to insert after a failed login to slow down brute force attacks (ms).
* #define LOGIN_SHELL_DELAY 3000
* Timing can be very important: the check should take the same amount of
* time for valid and invalid user identifiers or passwords so that an
* attacker cannot gain knowledge about the valid users on the system
* based on failed login attempts.
*
* \relates Shell
*/
/** /**
* \brief Constructs a new Shell instance. * \brief Constructs a new Shell instance.
@ -127,13 +121,16 @@
*/ */
Shell::Shell() Shell::Shell()
: maxHistory(0) : maxHistory(0)
, curStart(0)
, curLen(0) , curLen(0)
, curMax(sizeof(buffer))
, history(0) , history(0)
, historyWrite(0) , historyWrite(0)
, historyPosn(0) , historyPosn(0)
, prom("> ") , prom("$ ")
, hideChars(false)
, isClient(false) , isClient(false)
, lineMode(LINEMODE_NORMAL | LINEMODE_ECHO)
, timer(0)
{ {
} }
@ -142,6 +139,7 @@ Shell::Shell()
*/ */
Shell::~Shell() Shell::~Shell()
{ {
clearHistory();
delete [] history; delete [] history;
} }
@ -154,11 +152,11 @@ Shell::~Shell()
* 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. Default is Terminal::Serial. * Terminal::Telnet. Default is Terminal::Serial.
* \return Returns true if the shell was initialized, or false if there * \return Returns false if there is insufficient memory for the history
* is insufficient memory for the history stack. * stack. The session will continue but without command history.
* *
* This function will print the prompt() in preparation for entry of * This function will print the prompt() in preparation for entry of
* the first command. The default prompt is "> "; call setPrompt() * the first command. The default prompt is "$ "; call setPrompt()
* before begin() to change this: * before begin() to change this:
* *
* \code * \code
@ -213,29 +211,31 @@ bool Shell::beginShell(Stream &stream, size_t maxHistory, Terminal::Mode mode)
Terminal::begin(stream, mode); Terminal::begin(stream, mode);
// Create the history buffer. // Create the history buffer.
bool ok = true;
this->maxHistory = maxHistory; this->maxHistory = maxHistory;
delete [] history; delete [] history;
if (maxHistory) { if (maxHistory) {
history = new char [sizeof(buffer) * maxHistory]; history = new char [sizeof(buffer) * maxHistory];
if (!history) { if (history) {
Terminal::end(); memset(history, 0, sizeof(buffer) * maxHistory);
return false; } else {
this->maxHistory = 0;
ok = false;
} }
memset(history, 0, sizeof(buffer) * maxHistory);
} else { } else {
history = 0; history = 0;
} }
// Clear other variables. // Clear other variables.
curStart = 0;
curLen = 0; curLen = 0;
curMax = sizeof(buffer);
historyWrite = 0; historyWrite = 0;
historyPosn = 0; historyPosn = 0;
hideChars = false;
// Print the initial prompt. // Begins the login session.
if (prom) beginSession();
print(prom); return ok;
return true;
} }
/** /**
@ -249,14 +249,17 @@ bool Shell::beginShell(Stream &stream, size_t maxHistory, Terminal::Mode mode)
void Shell::end() void Shell::end()
{ {
Terminal::end(); Terminal::end();
clearHistory();
delete [] history; delete [] history;
maxHistory = 0; maxHistory = 0;
curStart = 0;
curLen = 0; curLen = 0;
curMax = sizeof(buffer);
history = 0; history = 0;
historyWrite = 0; historyWrite = 0;
historyPosn = 0; historyPosn = 0;
hideChars = false;
isClient = false; isClient = false;
lineMode = LINEMODE_NORMAL | LINEMODE_ECHO;
} }
/** @cond */ /** @cond */
@ -282,6 +285,21 @@ void Shell::loop()
return; return;
} }
// If the login delay is active, then suppress all input.
if (lineMode & LINEMODE_DELAY) {
if ((millis() - timer) >= LOGIN_SHELL_DELAY) {
lineMode &= ~LINEMODE_DELAY;
timer = 0;
} else {
readKey();
return;
}
}
// Print the prompt if necessary.
if (lineMode & LINEMODE_PROMPT)
printPrompt();
// 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
@ -309,12 +327,14 @@ void Shell::loop()
case 0x04: case 0x04:
// CTRL-D - equivalent to the "exit" command. // CTRL-D - equivalent to the "exit" command.
executeBuiltin(builtin_cmd_exit); if (lineMode & LINEMODE_NORMAL)
executeBuiltin(builtin_cmd_exit);
break; break;
case KEY_UP_ARROW: case KEY_UP_ARROW:
// Go back one item in the command history. // Go back one item in the command history.
if (!hideChars && history && historyPosn < maxHistory) { if ((lineMode & LINEMODE_NORMAL) != 0 &&
history && historyPosn < maxHistory) {
++historyPosn; ++historyPosn;
changeHistory(); changeHistory();
} }
@ -322,7 +342,8 @@ void Shell::loop()
case KEY_DOWN_ARROW: case KEY_DOWN_ARROW:
// Go forward one item in the command history. // Go forward one item in the command history.
if (!hideChars && history && historyPosn > 0) { if ((lineMode & LINEMODE_NORMAL) != 0 &&
history && historyPosn > 0) {
--historyPosn; --historyPosn;
changeHistory(); changeHistory();
} }
@ -330,16 +351,17 @@ void Shell::loop()
case KEY_F1: case KEY_F1:
// F1 is equivalent to the "help" command. // F1 is equivalent to the "help" command.
executeBuiltin(builtin_cmd_help); if (lineMode & LINEMODE_NORMAL)
executeBuiltin(builtin_cmd_help);
break; break;
case KEY_UNICODE: { case KEY_UNICODE: {
// Add the Unicode code point to the buffer if it will fit. // Add the Unicode code point to the buffer if it will fit.
long code = unicodeKey(); long code = unicodeKey();
size_t size = Terminal::utf8Length(code); size_t size = Terminal::utf8Length(code);
if (size && (curLen + size) < (sizeof(buffer) - 1)) { if (size && (curLen + size) < (curMax - 1)) {
Terminal::utf8Format((uint8_t *)(buffer + curLen), code); Terminal::utf8Format((uint8_t *)(buffer + curLen), code);
if (!hideChars) if (lineMode & LINEMODE_ECHO)
write((uint8_t *)(buffer + curLen), size); write((uint8_t *)(buffer + curLen), size);
curLen += size; curLen += size;
} }
@ -348,8 +370,8 @@ void Shell::loop()
default: default:
if (key >= 0x20 && key <= 0x7E) { if (key >= 0x20 && key <= 0x7E) {
// Printable ASCII character - echo and add it to the buffer. // Printable ASCII character - echo and add it to the buffer.
if (curLen < (sizeof(buffer) - 1)) { if (curLen < (curMax - 1)) {
if (!hideChars) if (lineMode & LINEMODE_ECHO)
write((uint8_t)key); write((uint8_t)key);
buffer[curLen++] = (char)key; buffer[curLen++] = (char)key;
} }
@ -458,7 +480,7 @@ void Shell::registerCommand(ShellCommandRegister *cmd)
* \fn const char *Shell::prompt() const * \fn const char *Shell::prompt() const
* \brief Gets the prompt string to display in the shell. * \brief Gets the prompt string to display in the shell.
* *
* \return The current prompt. The default is "> ". * \return The current prompt. The default is "$ ".
* *
* \sa setPrompt() * \sa setPrompt()
*/ */
@ -471,48 +493,11 @@ void Shell::registerCommand(ShellCommandRegister *cmd)
* that the string persists after this call returns. The Shell class does * that the string persists after this call returns. The Shell class does
* not make a copy of the string. * not make a copy of the string.
* *
* This function must be called before begin() or the first line will be * Calling this function will change the prompt for the next line of input.
* prompted with the default of "> ". Afterwards, calling this function
* will change the prompt for the following line of input.
* *
* \sa prompt() * \sa prompt()
*/ */
/**
* \fn bool Shell::hideCharacters() const
* \brief Determine if character echo is hidden.
*
* \sa setHideCharacters()
*/
/**
* \brief Enables or disables character hiding.
*
* \param hide Set to true to enable character hiding, or false to disable.
*
* When character hiding is enabled, all characters on a command-line are
* hidden and suppressed from being echoed. This is useful for the entry
* of passwords and other sensitive information.
*
* \sa hideCharacters()
*/
void Shell::setHideCharacters(bool hide)
{
if (hideChars == hide)
return;
hideChars = hide;
if (hide) {
// Hide the current command if something has already been echoed.
size_t len = curLen;
clearCharacters(len);
curLen = len;
} else {
// Print the current in-progress command to un-hide it.
if (curLen)
write((const uint8_t *)buffer, curLen);
}
}
/** /**
* \brief Displays help for all supported commands. * \brief Displays help for all supported commands.
*/ */
@ -557,9 +542,32 @@ void Shell::exit()
if (isClient) { if (isClient) {
end(); end();
((Client *)stream)->stop(); ((Client *)stream)->stop();
} else {
clearHistory();
println();
beginSession();
} }
} }
/**
* \brief Begins a login session.
*/
void Shell::beginSession()
{
// No login support in the base class, so enter normal mode immediately.
lineMode = LINEMODE_NORMAL | LINEMODE_ECHO | LINEMODE_PROMPT;
}
/**
* \brief Prints the current prompt string.
*/
void Shell::printPrompt()
{
if (prom)
print(prom);
lineMode &= ~LINEMODE_PROMPT;
}
/** /**
* \brief Executes the command in the buffer. * \brief Executes the command in the buffer.
*/ */
@ -573,12 +581,12 @@ void Shell::execute()
// If we have a history stack and the new command is different from // If we have a history stack and the new command is different from
// the previous command, then copy the command into the stack. // the previous command, then copy the command into the stack.
if (history && curLen > 0) { if (history && curLen > curStart) {
char *hist = history + sizeof(buffer) * historyWrite; char *hist = history + sizeof(buffer) * historyWrite;
if (strcmp(hist, buffer) != 0) { if (strcmp(hist, buffer) != 0) {
historyWrite = (historyWrite + 1) % maxHistory; historyWrite = (historyWrite + 1) % maxHistory;
hist = history + sizeof(buffer) * historyWrite; hist = history + sizeof(buffer) * historyWrite;
strcpy(hist, buffer); strcpy(hist, buffer + curStart);
} }
} }
@ -586,7 +594,7 @@ void Shell::execute()
historyPosn = 0; historyPosn = 0;
// Break the command up into arguments and populate the argument array. // Break the command up into arguments and populate the argument array.
ShellArguments argv(buffer, curLen); ShellArguments argv(buffer + curStart, curLen - curStart);
// Clear the line buffer. // Clear the line buffer.
curLen = 0; curLen = 0;
@ -610,9 +618,8 @@ void Shell::execute()
} }
} }
// Prepare for the next command. // Prepare to print the prompt for the next command.
if (prom) lineMode |= LINEMODE_PROMPT;
print(prom);
} }
/** /**
@ -646,8 +653,9 @@ void Shell::executeBuiltin(const char *cmd)
{ {
clearCharacters(curLen); clearCharacters(curLen);
curLen = strlen_P(cmd); curLen = strlen_P(cmd);
strncpy_P(buffer, cmd, curLen); strncpy_P(buffer + curStart, cmd, curLen);
write((const uint8_t *)buffer, curLen); write((const uint8_t *)(buffer + curStart), curLen);
curLen += curStart;
execute(); execute();
} }
@ -659,11 +667,11 @@ void Shell::executeBuiltin(const char *cmd)
void Shell::clearCharacters(size_t len) void Shell::clearCharacters(size_t len)
{ {
// If the characters are hidden, then there's nothing to backspace over. // If the characters are hidden, then there's nothing to backspace over.
if (hideChars) if (!(lineMode & LINEMODE_ECHO))
return; return;
// Backspace over all characters in the buffer. // Backspace over all characters in the buffer.
while (len > 0 && curLen > 0) { while (len > 0 && curLen > curStart) {
uint8_t ch = (uint8_t)(buffer[curLen - 1]); uint8_t ch = (uint8_t)(buffer[curLen - 1]);
if (ch < 0x80) { if (ch < 0x80) {
backspace(); backspace();
@ -716,9 +724,12 @@ void Shell::changeHistory()
// Copy the line from the history into the command buffer. // Copy the line from the history into the command buffer.
clearCharacters(curLen); clearCharacters(curLen);
curLen = strlen(hist); curLen = strlen(hist);
memcpy(buffer, hist, curLen); if (curLen > (curMax - curStart))
if (!hideChars) curLen = curMax - curStart;
memcpy(buffer + curStart, hist, curLen);
if (lineMode & LINEMODE_ECHO)
write((uint8_t *)hist, curLen); write((uint8_t *)hist, curLen);
curLen += curStart;
} else { } else {
// We've gone too far - the history is still smaller // We've gone too far - the history is still smaller
// than maxHistory in size. So reset the position and // than maxHistory in size. So reset the position and
@ -731,6 +742,22 @@ void Shell::changeHistory()
} }
} }
/**
* \brief Clears the command buffer and history.
*
* This clears the history so that commands from one login session do
* not leak into the next login session.
*/
void Shell::clearHistory()
{
if (history)
memset(history, 0, sizeof(buffer) * maxHistory);
historyPosn = 0;
historyWrite = 0;
clearCharacters(curLen);
memset(buffer, 0, sizeof(buffer));
}
/** /**
* \fn ShellCommand(name,help,function) * \fn ShellCommand(name,help,function)
* \brief Registers a command with the shell. * \brief Registers a command with the shell.
@ -868,3 +895,140 @@ const char *ShellArguments::operator[](int index) const
return line + currentPosn; return line + currentPosn;
} }
} }
void LoginShell::beginSession()
{
lineMode = LINEMODE_USERNAME | LINEMODE_ECHO | LINEMODE_PROMPT;
curStart = 0;
curLen = 0;
curMax = sizeof(buffer) / 2;
uid = -1;
}
/**
* \fn const char *LoginShell::machineName() const
* \brief Gets the name of the machine to display in the login prompt.
*
* The default value is NULL, indicating that no machine name should be shown.
*
* \sa setMachineName()
*/
/**
* \fn void LoginShell::setMachineName(const char *machineName)
* \brief Sets the name of the machine to display in the login prompt.
*
* \param machineName The machine name, or NULL for no machine name.
*
* \sa machineName()
*/
/**
* \fn ShellPasswordCheckFunc LoginShell::passwordCheckFunction() const
* \brief Gets the current password checking function, or NULL if the
* function has not been set yet.
*
* \sa setPasswordCheckFunction()
*/
/**
* \fn void LoginShell::setPasswordCheckFunction(ShellPasswordCheckFunc function)
* \brief Sets the password checking function.
*
* \param function The password checking function to set, or NULL to return
* to the default rules.
*
* If no function is set, then LoginShell will check for a username of
* "root" and a password of "arduino" (both values are case-sensitive).
* This is of course not very secure. Realistic applications should set a
* proper password checking function.
*
* \sa passwordCheckFunction()
*/
void LoginShell::printPrompt()
{
static char const loginString[] PROGMEM = "login: ";
static char const passwordString[] PROGMEM = "Password: ";
if (lineMode & LINEMODE_NORMAL) {
// Print the prompt for normal command entry.
if (prom)
print(prom);
// Normal commands occupy the full command buffer.
curStart = 0;
curLen = 0;
curMax = sizeof(buffer);
} else if (lineMode & LINEMODE_USERNAME) {
// Print the machine name and the login prompt.
if (machName) {
print(machName);
write((uint8_t)' ');
}
writeProgMem(loginString);
// Login name is placed into the first half of the line buffer.
curStart = 0;
curLen = 0;
curMax = sizeof(buffer) / 2;
} else if (lineMode & LINEMODE_PASSWORD) {
// Print the password prompt.
writeProgMem(passwordString);
// Password is placed into the second half of the line buffer.
curStart = sizeof(buffer) / 2;
curLen = curStart;
curMax = sizeof(buffer);
}
lineMode &= ~LINEMODE_PROMPT;
}
// Default password checking function. This is not a very good security check!
static int defaultPasswordCheckFunc(const char *username, const char *password)
{
static char const defaultUsername[] PROGMEM = "root";
static char const defaultPassword[] PROGMEM = "arduino";
if (!strcmp_P(username, defaultUsername) &&
!strcmp_P(password, defaultPassword)) {
return 0;
} else {
return -1;
}
}
void LoginShell::execute()
{
if (lineMode & LINEMODE_NORMAL) {
// Normal command execution.
Shell::execute();
} else if (lineMode & LINEMODE_USERNAME) {
// Prompting for the login username.
buffer[curLen] = '\0';
lineMode = LINEMODE_PASSWORD | LINEMODE_PROMPT;
println();
} else if (lineMode & LINEMODE_PASSWORD) {
// Prompting for the login password.
buffer[curLen] = '\0';
println();
// Check the user name and password.
int userid;
if (checkFunc)
userid = checkFunc(buffer, buffer + sizeof(buffer) / 2);
else
userid = defaultPasswordCheckFunc(buffer, buffer + sizeof(buffer) / 2);
// Clear the user name and password from memory after they are checked.
memset(buffer, 0, sizeof(buffer));
// Go to either normal mode or back to username mode.
if (userid >= 0) {
uid = userid;
lineMode = LINEMODE_NORMAL | LINEMODE_ECHO | LINEMODE_PROMPT;
} else {
lineMode = LINEMODE_USERNAME | LINEMODE_ECHO |
LINEMODE_PROMPT | LINEMODE_DELAY;
timer = millis();
}
}
}

View File

@ -28,6 +28,7 @@
class Shell; class Shell;
class ShellArguments; class ShellArguments;
class LoginShell;
#if defined(__arm__) #if defined(__arm__)
#define SHELL_MAX_CMD_LEN 256 #define SHELL_MAX_CMD_LEN 256
@ -36,7 +37,6 @@ class ShellArguments;
#endif #endif
typedef void (*ShellCommandFunc)(Shell &shell, int argc, const ShellArguments &argv); typedef void (*ShellCommandFunc)(Shell &shell, int argc, const ShellArguments &argv);
typedef bool (*ShellPasswordCheckFunc)(const char *userid, const char *password);
/** @cond */ /** @cond */
@ -76,33 +76,40 @@ public:
const char *prompt() const { return prom; } const char *prompt() const { return prom; }
void setPrompt(const char *prompt) { prom = prompt; } void setPrompt(const char *prompt) { prom = prompt; }
bool hideCharacters() const { return hideChars; }
void setHideCharacters(bool hide);
void help(); void help();
void exit(); void exit();
protected:
virtual void beginSession();
virtual void printPrompt();
virtual void execute();
private: private:
char buffer[SHELL_MAX_CMD_LEN]; char buffer[SHELL_MAX_CMD_LEN];
size_t maxHistory; size_t maxHistory;
size_t curStart;
size_t curLen; size_t curLen;
size_t curMax;
char *history; char *history;
size_t historyWrite; size_t historyWrite;
size_t historyPosn; size_t historyPosn;
const char *prom; const char *prom;
bool hideChars;
bool isClient; bool isClient;
uint8_t lineMode;
unsigned long timer;
// 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); bool beginShell(Stream &stream, size_t maxHistory, Terminal::Mode mode);
void execute();
bool execute(const ShellArguments &argv); bool execute(const ShellArguments &argv);
void executeBuiltin(const char *cmd); void executeBuiltin(const char *cmd);
void clearCharacters(size_t len); void clearCharacters(size_t len);
void changeHistory(); void changeHistory();
void clearHistory();
friend class LoginShell;
}; };
class ShellArguments class ShellArguments

View File

@ -8,6 +8,7 @@ This example is placed into the public domain.
#include <SPI.h> #include <SPI.h>
#include <Ethernet.h> #include <Ethernet.h>
#include <Shell.h> #include <Shell.h>
#include <LoginShell.h>
byte macAddress[6] = { byte macAddress[6] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
@ -19,7 +20,7 @@ EthernetServer server(23);
EthernetClient client; EthernetClient client;
bool haveClient = false; bool haveClient = false;
Shell shell; LoginShell shell;
void cmdLed(Shell &shell, int argc, const ShellArguments &argv) void cmdLed(Shell &shell, int argc, const ShellArguments &argv)
{ {
@ -50,9 +51,7 @@ void setup()
// Listen on port 23 for incoming telnet connections. // Listen on port 23 for incoming telnet connections.
server.begin(); server.begin();
shell.setMachineName("Arduino");
// Configure the shell. We call Shell::begin() once we have a connection.
shell.setPrompt("$ ");
} }
void loop() void loop()