[lib][console] move the state of the console into an object

This will allow in the future multiple instances of it to be active at
at a time. Place the current console in a new TLS slot per thread so
threads created as a side effect of console commands can properly run
commands.
This commit is contained in:
Travis Geiselbrecht
2021-05-28 18:43:47 -07:00
parent c49e63e62c
commit 89f9805277
5 changed files with 157 additions and 85 deletions

View File

@@ -10,7 +10,13 @@
#include <lib/console.h> #include <lib/console.h>
static void shell_entry(const struct app_descriptor *app, void *args) { static void shell_entry(const struct app_descriptor *app, void *args) {
console_start(); console_t *con = console_create(true);
if (!con)
return;
console_start(con);
// TODO: destroy console and free resources
} }
APP_START(shell) APP_START(shell)

View File

@@ -48,6 +48,9 @@ typedef int (*thread_start_routine)(void *arg);
/* thread local storage */ /* thread local storage */
enum thread_tls_list { enum thread_tls_list {
#ifdef WITH_LIB_CONSOLE
TLS_ENTRY_CONSOLE, // current console
#endif
#ifdef WITH_LIB_UTHREAD #ifdef WITH_LIB_UTHREAD
TLS_ENTRY_UTHREAD, TLS_ENTRY_UTHREAD,
#endif #endif

View File

@@ -202,7 +202,7 @@ thread_t *thread_create_etc(thread_t *t, const char *name, thread_start_routine
/* save whether or not we need to free the thread struct and/or stack */ /* save whether or not we need to free the thread struct and/or stack */
t->flags = flags; t->flags = flags;
/* inheirit thread local storage from the parent */ /* inherit thread local storage from the parent */
thread_t *current_thread = get_current_thread(); thread_t *current_thread = get_current_thread();
int i; int i;
for (i=0; i < MAX_TLS_ENTRY; i++) for (i=0; i < MAX_TLS_ENTRY; i++)

View File

@@ -21,6 +21,8 @@
#include <lib/env.h> #include <lib/env.h>
#endif #endif
#define LOCAL_TRACE 0
// Whether to enable command line history. Uses a nonzero // Whether to enable command line history. Uses a nonzero
// amount of memory, probably shouldn't enable for memory constrained devices. // amount of memory, probably shouldn't enable for memory constrained devices.
#ifndef CONSOLE_ENABLE_HISTORY #ifndef CONSOLE_ENABLE_HISTORY
@@ -42,28 +44,34 @@
#define WHITESPACE " \t" #define WHITESPACE " \t"
/* debug buffer */ // a single console instance
static char *debug_buffer; typedef struct console {
/* command processor state */
mutex_t lock;
int lastresult;
bool abort_script;
/* echo commands? */ /* debug buffer */
static bool echo = true; char *debug_buffer;
/* command processor state */ /* echo commands? */
static mutex_t command_lock = MUTEX_INITIAL_VALUE(command_lock); bool echo; // = true;
int lastresult;
static bool abort_script;
#if CONSOLE_ENABLE_HISTORY #if CONSOLE_ENABLE_HISTORY
/* command history stuff */ /* command history stuff */
#define HISTORY_LEN 16 #define HISTORY_LEN 16
static char history[HISTORY_LEN * LINE_LEN]; char history[HISTORY_LEN * LINE_LEN];
static uint history_next = 0; size_t history_next; // = 0;
#endif // CONSOLE_ENABLE_HISTORY
} console_t;
static void add_history(const char *line); #if CONSOLE_ENABLE_HISTORY
static uint start_history_cursor(void); /* command history routines */
static const char *next_history(uint *cursor); static void add_history(console_t *con, const char *line);
static const char *prev_history(uint *cursor); static uint start_history_cursor(console_t *con);
static void dump_history(void); static const char *next_history(console_t *con, uint *cursor);
static const char *prev_history(console_t *con, uint *cursor);
static void dump_history(console_t *con);
#endif #endif
/* a linear array of statically defined command blocks, /* a linear array of statically defined command blocks,
@@ -96,16 +104,16 @@ STATIC_COMMAND("history", "command history", &cmd_history)
STATIC_COMMAND("repeat", "repeats command multiple times", &cmd_repeat) STATIC_COMMAND("repeat", "repeats command multiple times", &cmd_repeat)
#endif #endif
#endif #endif
STATIC_COMMAND_END(help); STATIC_COMMAND_END(console);
#if CONSOLE_ENABLE_HISTORY #if CONSOLE_ENABLE_HISTORY
static int cmd_history(int argc, const console_cmd_args *argv) { static int cmd_history(int argc, const console_cmd_args *argv) {
dump_history(); dump_history(console_get_current());
return 0; return 0;
} }
static inline char *history_line(uint line) { static inline char *history_line(console_t *con, uint line) {
return history + line * LINE_LEN; return con->history + line * LINE_LEN;
} }
static inline uint ptrnext(uint ptr) { static inline uint ptrnext(uint ptr) {
@@ -116,57 +124,57 @@ static inline uint ptrprev(uint ptr) {
return (ptr - 1) % HISTORY_LEN; return (ptr - 1) % HISTORY_LEN;
} }
static void dump_history(void) { static void dump_history(console_t *con) {
printf("command history:\n"); printf("command history:\n");
uint ptr = ptrprev(history_next); uint ptr = ptrprev(con->history_next);
int i; int i;
for (i=0; i < HISTORY_LEN; i++) { for (i=0; i < HISTORY_LEN; i++) {
if (history_line(ptr)[0] != 0) if (history_line(con, ptr)[0] != 0)
printf("\t%s\n", history_line(ptr)); printf("\t%s\n", history_line(con, ptr));
ptr = ptrprev(ptr); ptr = ptrprev(ptr);
} }
} }
static void add_history(const char *line) { static void add_history(console_t *con, const char *line) {
// reject some stuff // reject some stuff
if (line[0] == 0) if (line[0] == 0)
return; return;
uint last = ptrprev(history_next); size_t last = ptrprev(con->history_next);
if (strcmp(line, history_line(last)) == 0) if (strcmp(line, history_line(con, last)) == 0)
return; return;
strlcpy(history_line(history_next), line, LINE_LEN); strlcpy(history_line(con, con->history_next), line, LINE_LEN);
history_next = ptrnext(history_next); con->history_next = ptrnext(con->history_next);
} }
static uint start_history_cursor(void) { static uint start_history_cursor(console_t *con) {
return ptrprev(history_next); return ptrprev(con->history_next);
} }
static const char *next_history(uint *cursor) { static const char *next_history(console_t *con, uint *cursor) {
uint i = ptrnext(*cursor); uint i = ptrnext(*cursor);
if (i == history_next) if (i == con->history_next)
return ""; // can't let the cursor hit the head return ""; // can't let the cursor hit the head
*cursor = i; *cursor = i;
return history_line(i); return history_line(con, i);
} }
static const char *prev_history(uint *cursor) { static const char *prev_history(console_t *con, uint *cursor) {
uint i; uint i;
const char *str = history_line(*cursor); const char *str = history_line(con, *cursor);
/* if we are already at head, stop here */ /* if we are already at head, stop here */
if (*cursor == history_next) if (*cursor == con->history_next)
return str; return str;
/* back up one */ /* back up one */
i = ptrprev(*cursor); i = ptrprev(*cursor);
/* if the next one is gonna be null */ /* if the next one is gonna be null */
if (history_line(i)[0] == '\0') if (history_line(con, i)[0] == '\0')
return str; return str;
/* update the cursor */ /* update the cursor */
@@ -175,6 +183,20 @@ static const char *prev_history(uint *cursor) {
} }
#endif // CONSOLE_ENABLE_HISTORY #endif // CONSOLE_ENABLE_HISTORY
console_t *console_get_current(void) {
console_t *con = (console_t *)tls_get(TLS_ENTRY_CONSOLE);
DEBUG_ASSERT(con);
return con;
}
console_t *console_set_current(console_t *con) {
console_t *old = (console_t *)tls_get(TLS_ENTRY_CONSOLE);
tls_set(TLS_ENTRY_CONSOLE, (uintptr_t)con);
LTRACEF("setting new %p, old %p\n", con, old);
return old;
}
#if CONSOLE_ENABLE_REPEAT #if CONSOLE_ENABLE_REPEAT
static int cmd_repeat(int argc, const console_cmd_args *argv) { static int cmd_repeat(int argc, const console_cmd_args *argv) {
if (argc < 4) goto usage; if (argc < 4) goto usage;
@@ -203,7 +225,7 @@ static int cmd_repeat(int argc, const console_cmd_args *argv) {
for (int i = 0; i < times; ++i) { for (int i = 0; i < times; ++i) {
printf("[%d/%d]\n", i + 1, times); printf("[%d/%d]\n", i + 1, times);
int result = console_run_script_locked(line); int result = console_run_script_locked(console_get_current(), line);
if (result != 0) { if (result != 0) {
printf("terminating repeat loop, command exited with status %d\n", printf("terminating repeat loop, command exited with status %d\n",
result); result);
@@ -238,11 +260,12 @@ static const console_cmd *match_command(const char *command, const uint8_t avail
static int read_debug_line(const char **outbuffer, void *cookie) { static int read_debug_line(const char **outbuffer, void *cookie) {
int pos = 0; int pos = 0;
int escape_level = 0; int escape_level = 0;
console_t *con = (console_t *)cookie;
#if CONSOLE_ENABLE_HISTORY #if CONSOLE_ENABLE_HISTORY
uint history_cursor = start_history_cursor(); uint history_cursor = start_history_cursor(con);
#endif #endif
char *buffer = debug_buffer; char *buffer = con->debug_buffer;
for (;;) { for (;;) {
/* loop until we get a char */ /* loop until we get a char */
@@ -256,7 +279,7 @@ static int read_debug_line(const char **outbuffer, void *cookie) {
switch (c) { switch (c) {
case '\r': case '\r':
case '\n': case '\n':
if (echo) if (con->echo)
putchar('\n'); putchar('\n');
goto done; goto done;
@@ -274,7 +297,7 @@ static int read_debug_line(const char **outbuffer, void *cookie) {
default: default:
buffer[pos++] = c; buffer[pos++] = c;
if (echo) if (con->echo)
putchar(c); putchar(c);
} }
} else if (escape_level == 1) { } else if (escape_level == 1) {
@@ -289,13 +312,13 @@ static int read_debug_line(const char **outbuffer, void *cookie) {
switch (c) { switch (c) {
case 67: // right arrow case 67: // right arrow
buffer[pos++] = ' '; buffer[pos++] = ' ';
if (echo) if (con->echo)
putchar(' '); putchar(' ');
break; break;
case 68: // left arrow case 68: // left arrow
if (pos > 0) { if (pos > 0) {
pos--; pos--;
if (echo) { if (con->echo) {
fputs("\b \b", stdout); // wipe out a character fputs("\b \b", stdout); // wipe out a character
} }
} }
@@ -306,17 +329,17 @@ static int read_debug_line(const char **outbuffer, void *cookie) {
// wipe out the current line // wipe out the current line
while (pos > 0) { while (pos > 0) {
pos--; pos--;
if (echo) { if (con->echo) {
fputs("\b \b", stdout); // wipe out a character fputs("\b \b", stdout); // wipe out a character
} }
} }
if (c == 65) if (c == 65)
strlcpy(buffer, prev_history(&history_cursor), LINE_LEN); strlcpy(buffer, prev_history(con, &history_cursor), LINE_LEN);
else else
strlcpy(buffer, next_history(&history_cursor), LINE_LEN); strlcpy(buffer, next_history(con, &history_cursor), LINE_LEN);
pos = strlen(buffer); pos = strlen(buffer);
if (echo) if (con->echo)
fputs(buffer, stdout); fputs(buffer, stdout);
break; break;
#endif #endif
@@ -342,7 +365,7 @@ done:
#if CONSOLE_ENABLE_HISTORY #if CONSOLE_ENABLE_HISTORY
// add to history // add to history
add_history(buffer); add_history(con, buffer);
#endif #endif
// return a pointer to our buffer // return a pointer to our buffer
@@ -543,7 +566,7 @@ static void convert_args(int argc, console_cmd_args *argv) {
} }
static status_t command_loop(int (*get_line)(const char **, void *), void *get_line_cookie, bool showprompt, bool locked) { static status_t command_loop(console_t *con, int (*get_line)(const char **, void *), void *get_line_cookie, bool showprompt, bool locked) {
bool exit; bool exit;
#if WITH_LIB_ENV #if WITH_LIB_ENV
bool report_result; bool report_result;
@@ -610,34 +633,34 @@ static status_t command_loop(int (*get_line)(const char **, void *), void *get_l
} }
if (!locked) if (!locked)
mutex_acquire(&command_lock); mutex_acquire(&con->lock);
abort_script = false; con->abort_script = false;
lastresult = command->cmd_callback(argc, args); con->lastresult = command->cmd_callback(argc, args);
#if WITH_LIB_ENV #if WITH_LIB_ENV
bool report_result; bool report_result;
env_get_bool("reportresult", &report_result, false); env_get_bool("reportresult", &report_result, false);
if (report_result) { if (report_result) {
if (lastresult < 0) if (con->lastresult < 0)
printf("FAIL %d\n", lastresult); printf("FAIL %d\n", con->lastresult);
else else
printf("PASS %d\n", lastresult); printf("PASS %d\n", con->lastresult);
} }
#endif #endif
#if WITH_LIB_ENV #if WITH_LIB_ENV
// stuff the result in an environment var // stuff the result in an environment var
env_set_int("?", lastresult, true); env_set_int("?", con->lastresult, true);
#endif #endif
// someone must have aborted the current script // someone must have aborted the current script
if (abort_script) if (con->abort_script)
exit = true; exit = true;
abort_script = false; con->abort_script = false;
if (!locked) if (!locked)
mutex_release(&command_lock); mutex_release(&con->lock);
} }
free(outbuf); free(outbuf);
@@ -655,22 +678,39 @@ no_mem_error:
return ERR_NO_MEMORY; return ERR_NO_MEMORY;
} }
void console_abort_script(void) { void console_abort_script(console_t *con) {
abort_script = true; if (!con) {
con = console_get_current();
}
con->abort_script = true;
} }
void console_start(void) { console_t *console_create(bool with_history) {
debug_buffer = malloc(LINE_LEN); console_t *con = calloc(1, sizeof(console_t));
if (!con) {
dprintf(INFO, "error allocating console object\n");
return NULL;
}
// initialize
mutex_init(&con->lock);
con->echo = true;
con->debug_buffer = malloc(LINE_LEN);
return con;
}
void console_start(console_t *con) {
dprintf(INFO, "entering main console loop\n"); dprintf(INFO, "entering main console loop\n");
console_set_current(con);
while (command_loop(&read_debug_line, NULL, true, false) == NO_ERROR) while (command_loop(con, &read_debug_line, con, true, false) == NO_ERROR)
; ;
dprintf(INFO, "exiting main console loop\n"); console_set_current(NULL);
free (debug_buffer); dprintf(INFO, "exiting main console loop\n");
} }
struct line_read_struct { struct line_read_struct {
@@ -706,7 +746,7 @@ static int fetch_next_line(const char **buffer, void *cookie) {
return bufpos; return bufpos;
} }
static int console_run_script_etc(const char *string, bool locked) { static int console_run_script_etc(console_t *con, const char *string, bool locked) {
struct line_read_struct lineread; struct line_read_struct lineread;
lineread.string = string; lineread.string = string;
@@ -714,19 +754,25 @@ static int console_run_script_etc(const char *string, bool locked) {
lineread.buffer = malloc(LINE_LEN); lineread.buffer = malloc(LINE_LEN);
lineread.buflen = LINE_LEN; lineread.buflen = LINE_LEN;
command_loop(&fetch_next_line, (void *)&lineread, false, locked); command_loop(con, &fetch_next_line, (void *)&lineread, false, locked);
free(lineread.buffer); free(lineread.buffer);
return lastresult; return con->lastresult;
} }
int console_run_script(const char *string) { int console_run_script(console_t *con, const char *string) {
return console_run_script_etc(string, false); if (!con) {
con = console_get_current();
}
return console_run_script_etc(con, string, false);
} }
int console_run_script_locked(const char *string) { int console_run_script_locked(console_t *con, const char *string) {
return console_run_script_etc(string, true); if (!con) {
con = console_get_current();
}
return console_run_script_etc(con, string, true);
} }
console_cmd_func console_get_command_handler(const char *commandstr) { console_cmd_func console_get_command_handler(const char *commandstr) {
@@ -767,7 +813,7 @@ static int cmd_help_panic(int argc, const console_cmd_args *argv) {
static int cmd_echo(int argc, const console_cmd_args *argv) { static int cmd_echo(int argc, const console_cmd_args *argv) {
if (argc > 1) if (argc > 1)
echo = argv[1].b; console_get_current()->echo = argv[1].b;
return NO_ERROR; return NO_ERROR;
} }

View File

@@ -19,18 +19,35 @@ __BEGIN_CDECLS
#include <lk/console_cmd.h> #include <lk/console_cmd.h>
#include <lib/console/cmd.h> #include <lib/console/cmd.h>
/* external api */ typedef struct console console_t;
void console_start(void);
int console_run_script(const char *string); /* create an instance of the console */
int console_run_script_locked(const char *string); // special case from inside a command /* TODO: actually implement the history option. Currently always implements history according
* to the build variable CONSOLE_ENABLE_HISTORY. */
console_t *console_create(bool with_history);
/* Run the main console loop. Will set the current console TLS pointer as a side effect */
void console_start(console_t *con);
/* Routines to let code directly run commands in an existing console */
/* NOTE: Passing null as first argument selects the current console associated with the current thread */
int console_run_script(console_t *con, const char *string);
int console_run_script_locked(console_t *con, const char *string); // special case from inside a command
void console_abort_script(console_t *con);
/* Get/set the current console in the thread's TLS slot reserved for it.
* New threads will inherit the pointer from the parent thread.
*
* TODO: use a ref count to keep the console from being destroyed from underneath it.
*/
console_t *console_get_current(void);
console_t *console_set_current(console_t *con); // returns old console pointer
console_cmd_func console_get_command_handler(const char *command); console_cmd_func console_get_command_handler(const char *command);
void console_abort_script(void);
/* panic shell api */ /* panic shell api */
void panic_shell_start(void); void panic_shell_start(void);
extern int lastresult;
/* enable the panic shell if we're being built */ /* enable the panic shell if we're being built */
#if !defined(ENABLE_PANIC_SHELL) && PLATFORM_SUPPORTS_PANIC_SHELL #if !defined(ENABLE_PANIC_SHELL) && PLATFORM_SUPPORTS_PANIC_SHELL
#define ENABLE_PANIC_SHELL 1 #define ENABLE_PANIC_SHELL 1