Sysfs: - New VFS driver mounted at /sys that lets kernel drivers expose virtual text files via namespace registration - Drivers call sysfs_register(name, ops, ctx) with list/read/write callbacks for their namespace - IDE driver registers 'ide' namespace exposing per-device attributes: model, type, channel, drive, sectors, sector_size - Tested: ls /sys -> ide, ls /sys/ide -> hdd1 cd1, cat /sys/ide/hdd1/model -> QEMU HARDDISK Syscalls: - Added SYS_OPEN (11) and SYS_CLOSE (12) for file I/O from userspace - Extended SYS_READ/SYS_WRITE to handle VFS file descriptors (fd >= 3) - Updated userspace syscalls.h with open()/close() wrappers Apps: - New 'cat' app: reads and displays file contents via open/read/close - Updated 'ls' to accept path argument via ARG1 env var - Updated shell to pass ARG1 env var to external commands
188 lines
4.5 KiB
C
188 lines
4.5 KiB
C
/**
|
|
* @file sh.c
|
|
* @brief ClaudeOS shell.
|
|
*
|
|
* A simple interactive shell that reads commands from stdin,
|
|
* supports built-in commands (cd, exit, help, env), and
|
|
* executes external programs via fork+exec.
|
|
*/
|
|
|
|
#include "syscalls.h"
|
|
|
|
/** Maximum command line length. */
|
|
#define CMD_MAX 256
|
|
|
|
/** Read a line from stdin with echo and basic line editing.
|
|
* Returns length of the line (excluding newline). */
|
|
static int readline(char *buf, int maxlen) {
|
|
int pos = 0;
|
|
while (pos < maxlen - 1) {
|
|
char c;
|
|
int n = read(0, &c, 1);
|
|
if (n <= 0) {
|
|
/* No data yet: yield CPU and retry */
|
|
yield();
|
|
continue;
|
|
}
|
|
|
|
if (c == '\n' || c == '\r') {
|
|
putchar('\n');
|
|
break;
|
|
} else if (c == '\b' || c == 127) {
|
|
/* Backspace */
|
|
if (pos > 0) {
|
|
pos--;
|
|
puts("\b \b"); /* Move back, overwrite with space, move back */
|
|
}
|
|
} else if (c >= 32) {
|
|
/* Printable character */
|
|
buf[pos++] = c;
|
|
putchar(c);
|
|
}
|
|
}
|
|
buf[pos] = '\0';
|
|
return pos;
|
|
}
|
|
|
|
/** Skip leading whitespace, return pointer to first non-space. */
|
|
static char *skip_spaces(char *s) {
|
|
while (*s == ' ' || *s == '\t') s++;
|
|
return s;
|
|
}
|
|
|
|
/** Find the next space or end of string. */
|
|
static char *find_space(char *s) {
|
|
while (*s && *s != ' ' && *s != '\t') s++;
|
|
return s;
|
|
}
|
|
|
|
/** Built-in: cd <path> */
|
|
static void builtin_cd(char *arg) {
|
|
if (!arg || !*arg) {
|
|
arg = "/";
|
|
}
|
|
|
|
/* Update CWD environment variable */
|
|
setenv("CWD", arg);
|
|
/* Verify it was set */
|
|
char cwd[128];
|
|
if (getenv("CWD", cwd, sizeof(cwd)) >= 0) {
|
|
puts("cd: ");
|
|
puts(cwd);
|
|
putchar('\n');
|
|
}
|
|
}
|
|
|
|
/** Built-in: env - print all known env vars. */
|
|
static void builtin_env(void) {
|
|
char buf[128];
|
|
if (getenv("CWD", buf, sizeof(buf)) >= 0) {
|
|
puts("CWD=");
|
|
puts(buf);
|
|
putchar('\n');
|
|
}
|
|
if (getenv("PATH", buf, sizeof(buf)) >= 0) {
|
|
puts("PATH=");
|
|
puts(buf);
|
|
putchar('\n');
|
|
}
|
|
if (getenv("TEST", buf, sizeof(buf)) >= 0) {
|
|
puts("TEST=");
|
|
puts(buf);
|
|
putchar('\n');
|
|
}
|
|
}
|
|
|
|
/** Built-in: help */
|
|
static void builtin_help(void) {
|
|
puts("ClaudeOS Shell\n");
|
|
puts("Built-in commands:\n");
|
|
puts(" cd <path> - change working directory\n");
|
|
puts(" env - show environment variables\n");
|
|
puts(" help - show this message\n");
|
|
puts(" exit - exit the shell\n");
|
|
puts("External commands are loaded from initrd.\n");
|
|
}
|
|
|
|
/** Execute an external command via fork+exec. */
|
|
static void run_command(const char *cmd, const char *arg) {
|
|
int32_t pid = fork();
|
|
if (pid < 0) {
|
|
puts("sh: fork failed\n");
|
|
return;
|
|
}
|
|
|
|
if (pid == 0) {
|
|
/* Child: set ARG1 if there's an argument */
|
|
if (arg && *arg) {
|
|
setenv("ARG1", arg);
|
|
} else {
|
|
setenv("ARG1", "");
|
|
}
|
|
|
|
/* exec the command */
|
|
int32_t ret = exec(cmd);
|
|
if (ret < 0) {
|
|
puts("sh: ");
|
|
puts(cmd);
|
|
puts(": not found\n");
|
|
exit(127);
|
|
}
|
|
/* exec doesn't return on success */
|
|
exit(1);
|
|
}
|
|
|
|
/* Parent: wait for child */
|
|
waitpid(pid);
|
|
}
|
|
|
|
int main(void) {
|
|
puts("ClaudeOS Shell v0.1\n");
|
|
puts("Type 'help' for available commands.\n\n");
|
|
|
|
char cmd[CMD_MAX];
|
|
|
|
for (;;) {
|
|
/* Print prompt with CWD */
|
|
char cwd[128];
|
|
if (getenv("CWD", cwd, sizeof(cwd)) < 0) {
|
|
strcpy(cwd, "/");
|
|
}
|
|
puts(cwd);
|
|
puts("$ ");
|
|
|
|
/* Read command */
|
|
int len = readline(cmd, CMD_MAX);
|
|
if (len == 0) continue;
|
|
|
|
/* Parse command and arguments */
|
|
char *line = skip_spaces(cmd);
|
|
if (*line == '\0') continue;
|
|
|
|
char *arg_start = find_space(line);
|
|
char *arg = (char *)0;
|
|
if (*arg_start) {
|
|
*arg_start = '\0';
|
|
arg = skip_spaces(arg_start + 1);
|
|
if (*arg == '\0') arg = (char *)0;
|
|
}
|
|
|
|
/* Check built-in commands */
|
|
if (strcmp(line, "exit") == 0) {
|
|
puts("Goodbye!\n");
|
|
exit(0);
|
|
} else if (strcmp(line, "cd") == 0) {
|
|
builtin_cd(arg);
|
|
} else if (strcmp(line, "env") == 0) {
|
|
builtin_env();
|
|
} else if (strcmp(line, "help") == 0) {
|
|
builtin_help();
|
|
} else {
|
|
/* External command */
|
|
run_command(line, arg);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|