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:
parent
9221104977
commit
ab914c4d07
91
libraries/Terminal/LoginShell.cpp
Normal file
91
libraries/Terminal/LoginShell.cpp
Normal 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
|
||||||
|
*/
|
55
libraries/Terminal/LoginShell.h
Normal file
55
libraries/Terminal/LoginShell.h
Normal 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
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user