feat(shell): add in-kernel interactive shell
This commit is contained in:
1 parent
8e03bfc320
commit
d5b9cad635
2 files changed
+282
No files matched your search
@@ -0,0 +1,6 @@
|
||||
#ifndef FROSTBYTE_SHELL_H
|
||||
#define FROSTBYTE_SHELL_H
|
||||
|
||||
void shellRun(void);
|
||||
|
||||
#endif
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
#include "shell.h"
|
||||
#include "console.h"
|
||||
#include "keyboard.h"
|
||||
#include "vga.h"
|
||||
#include "pmm.h"
|
||||
#include "heap.h"
|
||||
#include "pit.h"
|
||||
#include "sched.h"
|
||||
#include "string.h"
|
||||
#include "vfs.h"
|
||||
#include "fs.h"
|
||||
|
||||
#define lineCapacity 128
|
||||
#define pathCapacity 256
|
||||
|
||||
static char cwd[pathCapacity] = "/";
|
||||
|
||||
static void printHelp(void)
|
||||
{
|
||||
kprintf("available commands:\n");
|
||||
kprintf(" help show this list\n");
|
||||
kprintf(" echo TEXT print the given text\n");
|
||||
kprintf(" echo TEXT >F write the text to file F\n");
|
||||
kprintf(" ls [PATH] list a directory\n");
|
||||
kprintf(" cat FILE print a file\n");
|
||||
kprintf(" mkdir PATH create a directory (writable mounts only)\n");
|
||||
kprintf(" cd PATH change the working directory\n");
|
||||
kprintf(" pwd print the working directory\n");
|
||||
kprintf(" mem physical memory and heap usage\n");
|
||||
kprintf(" uptime ticks and seconds since boot\n");
|
||||
kprintf(" ps list kernel threads\n");
|
||||
kprintf(" clear clear the screen\n");
|
||||
}
|
||||
|
||||
static void printMem(void)
|
||||
{
|
||||
uint64 totalKib = pmmTotalBytes() / 1024;
|
||||
uint64 usedKib = pmmUsedFrames() * frameSize / 1024;
|
||||
uint64 freeKib = pmmFreeFrames() * frameSize / 1024;
|
||||
|
||||
kprintf("physical memory: %lu KiB total\n", totalKib);
|
||||
kprintf(" frames: %lu total, %lu used, %lu free\n",
|
||||
pmmTotalFrames(), pmmUsedFrames(), pmmFreeFrames());
|
||||
kprintf(" used %lu KiB, free %lu KiB\n", usedKib, freeKib);
|
||||
kprintf("heap: %lu KiB used of %lu KiB\n",
|
||||
heapUsed() / 1024, heapTotal() / 1024);
|
||||
}
|
||||
|
||||
static void printUptime(void)
|
||||
{
|
||||
uint64 ticks = pitTicks();
|
||||
uint32 frequency = pitFrequency();
|
||||
kprintf("uptime: %lu ticks, %lu seconds (at %u Hz)\n",
|
||||
ticks, ticks / frequency, frequency);
|
||||
}
|
||||
|
||||
static void printThreads(void)
|
||||
{
|
||||
kprintf("id state name\n");
|
||||
for (uint32 i = 0; i < threadCount(); i++) {
|
||||
Thread* thread = threadAt(i);
|
||||
const char* state = thread->state == threadRunning ? "running" : "runnable";
|
||||
char marker = thread->id == currentThreadId() ? '*' : ' ';
|
||||
kprintf("%c%u %s %s\n", marker, thread->id, state, thread->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void buildPath(const char* argument, char* out)
|
||||
{
|
||||
if (argument[0] == '/') {
|
||||
uint64 length = strlen(argument);
|
||||
if (length >= pathCapacity)
|
||||
length = pathCapacity - 1;
|
||||
memcpy(out, argument, length);
|
||||
out[length] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 pos = strlen(cwd);
|
||||
if (pos >= pathCapacity)
|
||||
pos = pathCapacity - 1;
|
||||
memcpy(out, cwd, pos);
|
||||
if (pos == 0 || out[pos - 1] != '/') {
|
||||
if (pos < pathCapacity - 1)
|
||||
out[pos++] = '/';
|
||||
}
|
||||
for (uint64 i = 0; argument[i] && pos < pathCapacity - 1; i++)
|
||||
out[pos++] = argument[i];
|
||||
out[pos] = 0;
|
||||
}
|
||||
|
||||
static void listDir(const char* argument)
|
||||
{
|
||||
char path[pathCapacity];
|
||||
if (argument[0] == 0)
|
||||
memcpy(path, cwd, strlen(cwd) + 1);
|
||||
else
|
||||
buildPath(argument, path);
|
||||
|
||||
VfsNode* node = vfsLookup(path);
|
||||
if (!node) {
|
||||
kprintf("ls: no such path: %s\n", path);
|
||||
return;
|
||||
}
|
||||
if (!vfsIsDir(node)) {
|
||||
kprintf("%s %lu\n", vfsName(node), vfsSize(node));
|
||||
return;
|
||||
}
|
||||
for (VfsNode* child = vfsChild(node); child; child = vfsNext(child)) {
|
||||
if (vfsIsDir(child))
|
||||
kprintf("%s/\n", vfsName(child));
|
||||
else
|
||||
kprintf("%s %lu\n", vfsName(child), vfsSize(child));
|
||||
}
|
||||
}
|
||||
|
||||
static void catFile(const char* argument)
|
||||
{
|
||||
if (argument[0] == 0) {
|
||||
kprintf("cat: missing file\n");
|
||||
return;
|
||||
}
|
||||
char path[pathCapacity];
|
||||
buildPath(argument, path);
|
||||
|
||||
VfsNode* node = vfsLookup(path);
|
||||
if (!node) {
|
||||
kprintf("cat: no such file: %s\n", path);
|
||||
return;
|
||||
}
|
||||
if (vfsIsDir(node)) {
|
||||
kprintf("cat: %s is a directory\n", path);
|
||||
return;
|
||||
}
|
||||
const char* data = (const char*)vfsFileData(node);
|
||||
uint64 size = vfsSize(node);
|
||||
for (uint64 i = 0; i < size; i++)
|
||||
kputchar(data[i]);
|
||||
}
|
||||
|
||||
static void echoWrite(char* text, const char* target)
|
||||
{
|
||||
char path[pathCapacity];
|
||||
buildPath(target, path);
|
||||
|
||||
int fd = vfsOpen(path, oCreate | oWriteOnly | oTrunc);
|
||||
if (fd < 0) {
|
||||
kprintf("echo: cannot write %s\n", path);
|
||||
return;
|
||||
}
|
||||
uint64 length = strlen(text);
|
||||
vfsWrite(fd, text, length);
|
||||
vfsWrite(fd, "\n", 1);
|
||||
vfsClose(fd);
|
||||
}
|
||||
|
||||
static void makeDir(const char* argument)
|
||||
{
|
||||
if (argument[0] == 0) {
|
||||
kprintf("mkdir: missing path\n");
|
||||
return;
|
||||
}
|
||||
char path[pathCapacity];
|
||||
buildPath(argument, path);
|
||||
if (vfsMkdir(path) != 0)
|
||||
kprintf("mkdir: cannot create %s\n", path);
|
||||
}
|
||||
|
||||
static void changeDir(const char* argument)
|
||||
{
|
||||
char path[pathCapacity];
|
||||
if (argument[0] == 0)
|
||||
memcpy(path, "/", 2);
|
||||
else
|
||||
buildPath(argument, path);
|
||||
|
||||
VfsNode* node = vfsLookup(path);
|
||||
if (!node || !vfsIsDir(node)) {
|
||||
kprintf("cd: no such directory: %s\n", path);
|
||||
return;
|
||||
}
|
||||
vfsPath(node, cwd, sizeof(cwd));
|
||||
}
|
||||
|
||||
static void doEcho(char* argument)
|
||||
{
|
||||
char* redirect = argument;
|
||||
while (*redirect && *redirect != '>')
|
||||
redirect++;
|
||||
|
||||
if (*redirect == '>') {
|
||||
*redirect = 0;
|
||||
char* target = redirect + 1;
|
||||
while (*target == ' ')
|
||||
target++;
|
||||
|
||||
char* textEnd = redirect;
|
||||
while (textEnd > argument && textEnd[-1] == ' ')
|
||||
textEnd--;
|
||||
*textEnd = 0;
|
||||
|
||||
echoWrite(argument, target);
|
||||
} else {
|
||||
kprintf("%s\n", argument);
|
||||
}
|
||||
}
|
||||
|
||||
static void execute(char* line)
|
||||
{
|
||||
if (line[0] == 0)
|
||||
return;
|
||||
|
||||
char* argument = line;
|
||||
while (*argument && *argument != ' ')
|
||||
argument++;
|
||||
if (*argument == ' ') {
|
||||
*argument = 0;
|
||||
argument++;
|
||||
}
|
||||
while (*argument == ' ')
|
||||
argument++;
|
||||
|
||||
if (strcmp(line, "help") == 0)
|
||||
printHelp();
|
||||
else if (strcmp(line, "echo") == 0)
|
||||
doEcho(argument);
|
||||
else if (strcmp(line, "ls") == 0)
|
||||
listDir(argument);
|
||||
else if (strcmp(line, "cat") == 0)
|
||||
catFile(argument);
|
||||
else if (strcmp(line, "mkdir") == 0)
|
||||
makeDir(argument);
|
||||
else if (strcmp(line, "cd") == 0)
|
||||
changeDir(argument);
|
||||
else if (strcmp(line, "pwd") == 0)
|
||||
kprintf("%s\n", cwd);
|
||||
else if (strcmp(line, "mem") == 0)
|
||||
printMem();
|
||||
else if (strcmp(line, "uptime") == 0)
|
||||
printUptime();
|
||||
else if (strcmp(line, "ps") == 0)
|
||||
printThreads();
|
||||
else if (strcmp(line, "clear") == 0)
|
||||
vgaClear();
|
||||
else
|
||||
kprintf("unknown command: %s (try 'help')\n", line);
|
||||
}
|
||||
|
||||
void shellRun(void)
|
||||
{
|
||||
char line[lineCapacity];
|
||||
uint32 length = 0;
|
||||
|
||||
kprintf("\nfrostbyte shell ready. type 'help'.\n");
|
||||
kprintf("frostbyte> ");
|
||||
|
||||
for (;;) {
|
||||
char c = keyboardReadChar();
|
||||
|
||||
if (c == '\n') {
|
||||
kputchar('\n');
|
||||
line[length] = 0;
|
||||
execute(line);
|
||||
length = 0;
|
||||
kprintf("frostbyte> ");
|
||||
} else if (c == '\b') {
|
||||
if (length > 0) {
|
||||
length--;
|
||||
kprintf("\b \b");
|
||||
}
|
||||
} else if (length < lineCapacity - 1) {
|
||||
line[length++] = c;
|
||||
kputchar(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user