Pull code from yggdrasil userspace

This commit is contained in:
Mark
2020-07-02 19:24:02 +03:00
parent e58fdd591d
commit 95bdb68de9
12 changed files with 957 additions and 9 deletions
+8 -3
View File
@@ -2,12 +2,17 @@ CC?=$(CROSS_COMPILE)gcc
CFLAGS?=-ggdb \
-O0 \
-Wall \
-Werror
-Werror \
-Iinclude
LDFLAGS?=-lgcc
O=build
sh_OBJS=$(O)/sh.o
sh_OBJS=$(O)/sh.o \
$(O)/readline.o \
$(O)/cmd.o \
$(O)/parse.o \
$(O)/builtin.o
all: mkdirs $(O)/sh
@@ -23,5 +28,5 @@ install: $(O)/sh
$(O)/sh: $(sh_OBJS)
$(CC) $(LDFLAGS) -o $@ $(sh_OBJS)
$(O)/%.o: %.c
$(O)/%.o: src/%.c
$(CC) $(CFLAGS) -c -o $@ $<
+7
View File
@@ -0,0 +1,7 @@
#pragma once
struct cmd_unit;
typedef int (*builtin_func_t) (const struct cmd_unit *);
builtin_func_t builtin_find(const char *name);
+14
View File
@@ -0,0 +1,14 @@
#pragma once
//struct cmd_exec {
// int in_fd, out_fd, err_fd;
// size_t argc;
// char *args[12];
//};
//
//struct cmd_link {
// struct cmd_exec cmd;
// struct cmd_link *prev, *next;
//};
int eval(char *str);
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#define COLOR_RED "\033[31m"
#define COLOR_GREEN "\033[32m"
#define COLOR_YELLOW "\033[33m"
#define COLOR_MAGENTA "\033[35m"
#define COLOR_CYAN "\033[36m"
#define COLOR_RESET "\033[0m"
// TODO: take this from env (kernel does not support env yet)
#define PATH "/bin"
#define PS1 COLOR_MAGENTA "%u" COLOR_RESET \
"@" \
COLOR_CYAN "%h" COLOR_RESET " " \
COLOR_YELLOW "%d" COLOR_RESET \
" %# "
+15
View File
@@ -0,0 +1,15 @@
#pragma once
struct cmd_unit {
int fds[3];
int pid, res;
int argc;
char *args[64];
struct cmd_unit *prev, *next;
};
struct cmd {
struct cmd_unit *first, *last;
};
int cmd_parse(const char *str, struct cmd *cmd);
+3
View File
@@ -0,0 +1,3 @@
#pragma once
int readline(char *buf, size_t lim);
-6
View File
@@ -1,6 +0,0 @@
#include <stdio.h>
int main(int argc, char **argv) {
printf("This is a stub\n");
return 0;
}
+297
View File
@@ -0,0 +1,297 @@
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include "builtin.h"
#include "config.h"
#include "parse.h"
#include "cmd.h"
#define DEF_BUILTIN(b_name) \
static int __bcmd_##b_name(const struct cmd_unit *cmd)
#define DECL_BUILTIN(b_name) \
{ .name = #b_name, .exec = __bcmd_##b_name }
extern char **environ;
struct sh_builtin {
const char *name;
int (*exec) (const struct cmd_unit *cmd);
};
static struct sh_builtin __builtins[];
////
DEF_BUILTIN(env) {
for (size_t i = 0; environ[i]; ++i) {
printf("%s\n", environ[i]);
}
return 0;
}
// XXX: Broken
//DEF_BUILTIN(exit) {
// if (cmd->argc > 1) {
// exit(atoi(cmd->args[1]));
// }
// exit(0);
//}
DEF_BUILTIN(cd) {
if (cmd->argc != 2) {
printf("usage: cd <path>\n");
return -1;
}
return chdir(cmd->args[1]);
}
DEF_BUILTIN(cat) {
if (cmd->argc < 2) {
printf("usage: cat <filename> ...\n");
return -1;
}
char buf[4096];
ssize_t bread;
for (int i = 1; i < cmd->argc; ++i) {
int fd = open(cmd->args[i], O_RDONLY, 0);
if (fd < 0) {
perror(cmd->args[i]);
continue;
}
while ((bread = read(fd, buf, sizeof(buf))) > 0) {
write(STDOUT_FILENO, buf, bread);
}
if (bread < 0) {
perror(cmd->args[i]);
}
close(fd);
}
return 0;
}
DEF_BUILTIN(chmod) {
mode_t mode = 0;
const char *p;
if (cmd->argc != 3) {
printf("usage: chmod <mode> <filename>\n");
return -1;
}
p = cmd->args[1];
while (*p) {
if (*p > '7' || *p < '0') {
printf("Invalid mode: %s\n", cmd->args[1]);
return -1;
}
mode <<= 3;
mode |= *p - '0';
++p;
}
if (chmod(cmd->args[2], mode)) {
perror(cmd->args[2]);
return -1;
}
return 0;
}
DEF_BUILTIN(stat) {
struct stat st;
if (cmd->argc != 2) {
printf("usage: stat <filename>\n");
return -1;
}
if (stat(cmd->args[1], &st) != 0) {
return -1;
}
printf("device %u\n", (unsigned int) st.st_dev); // TODO: __dev_t
printf("inode %u\n", (unsigned int) st.st_ino); // TODO: __ino_t
printf("mode %u\n", st.st_mode);
printf("nlink %u\n", (unsigned int) st.st_nlink); // TODO: __nlink_t
printf("uid %u\n", st.st_uid);
printf("gid %u\n", st.st_gid);
printf("rdev %u\n", (unsigned int) st.st_rdev); // TODO: __dev_t
printf("size %u\n", (unsigned int) st.st_size); // TODO: off_t here
printf("atime %u\n", (unsigned int) st.st_atime);
printf("mtime %u\n", (unsigned int) st.st_mtime);
printf("ctime %u\n", (unsigned int) st.st_ctime);
printf("blksize %u\n", (unsigned int) st.st_blksize); // TODO: __blksize_t
printf("blocks %u\n", (unsigned int) st.st_blocks); // TODO: __blkcnt_t
return 0;
}
DEF_BUILTIN(sync) {
sync();
return 0;
}
DEF_BUILTIN(touch) {
int fd;
if (cmd->argc != 2) {
printf("usage: touch <filename>\n");
return -1;
}
if ((fd = open(cmd->args[1], O_WRONLY | O_CREAT, 0755)) < 0) {
perror(cmd->args[1]);
return -1;
}
close(fd);
return 0;
}
DEF_BUILTIN(chown) {
uid_t uid;
gid_t gid;
const char *p;
if (cmd->argc != 3) {
printf("usage: chown <uid>:<gid> <filename>\n");
return -1;
}
if (!(p = strchr(cmd->args[1], ':'))) {
printf("Invalid UID:GID pair: %s\n", cmd->args[1]);
return -1;
}
uid = atoi(cmd->args[1]);
gid = atoi(p + 1);
if (chown(cmd->args[2], uid, gid)) {
perror(cmd->args[2]);
return -1;
}
return 0;
}
DEF_BUILTIN(exec) {
//struct cmd_exec new_cmd;
if (cmd->argc < 2) {
printf("usage: exec <command> ...\n");
}
char path_path[256];
int res;
if (cmd->args[1][0] == '.' || cmd->args[1][0] == '/') {
if ((res = access(cmd->args[1], X_OK)) != 0) {
perror(cmd->args[1]);
return res;
}
strcpy(path_path, cmd->args[1]);
exit(execve(path_path, (char *const *) &cmd->args[1], NULL));
}
snprintf(path_path, sizeof(path_path), "%s/%s", PATH, cmd->args[1]);
if ((res = access(path_path, X_OK)) == 0) {
exit(execve(path_path, (char *const *) &cmd->args[1], NULL));
}
return -1;
}
DEF_BUILTIN(clear) {
printf("\033[2J\033[1;1f");
return 0;
}
DEF_BUILTIN(echo) {
for (int i = 1; i < cmd->argc; ++i) {
printf("%s ", cmd->args[i]);
}
printf("\n");
return 0;
}
DEF_BUILTIN(set) {
if (cmd->argc != 3) {
fprintf(stderr, "Usage: set <key> <value>\n");
return -1;
}
setenv(cmd->args[1], cmd->args[2], 1);
return 0;
}
// TODO: support usernames (getpwnam_r)
DEF_BUILTIN(setid) {
if (cmd->argc == 2) {
// Assume gid == uid
if (setuid(atoi(cmd->args[1])) != 0) {
return -1;
}
if (setgid(atoi(cmd->args[1])) != 0) {
return -1;
}
return 0;
}
if (cmd->argc != 3) {
printf("usage: setid <uid> <gid>\n");
return -1;
}
if (setuid(atoi(cmd->args[1])) != 0) {
return -1;
}
if (setgid(atoi(cmd->args[2])) != 0) {
return -1;
}
return 0;
}
DEF_BUILTIN(builtins) {
for (size_t i = 0; __builtins[i].name; ++i) {
printf("%s ", __builtins[i].name);
}
printf("\n");
return 0;
}
////
static struct sh_builtin __builtins[] = {
DECL_BUILTIN(builtins),
DECL_BUILTIN(cat),
DECL_BUILTIN(cd),
DECL_BUILTIN(chmod),
DECL_BUILTIN(chown),
DECL_BUILTIN(clear),
DECL_BUILTIN(echo),
DECL_BUILTIN(env),
DECL_BUILTIN(exec),
// DECL_BUILTIN(exit),
DECL_BUILTIN(set),
DECL_BUILTIN(setid),
DECL_BUILTIN(stat),
DECL_BUILTIN(sync),
DECL_BUILTIN(touch),
{NULL}
};
builtin_func_t builtin_find(const char *name) {
for (size_t i = 0; i < sizeof(__builtins) / sizeof(__builtins[0]); ++i) {
if (!__builtins[i].name) {
return NULL;
}
if (!strcmp(__builtins[i].name, name)) {
return __builtins[i].exec;
}
}
return NULL;
}
+203
View File
@@ -0,0 +1,203 @@
#include <sys/termios.h>
#include <sys/prctl.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <ctype.h>
#include <stdio.h>
#include "builtin.h"
#include "config.h"
#include "parse.h"
#include "cmd.h"
typedef int (*spawn_func_t) (void *arg, struct cmd_unit *cmd);
extern char **environ;
static int spawn_binary(void *arg, struct cmd_unit *cmd) {
return execve(arg, (char *const *) cmd->args, environ);
}
static int spawn_builtin(void *arg, struct cmd_unit *cmd) {
builtin_func_t func = arg;
return func(cmd);
}
static int cmd_spawn(spawn_func_t fn, void *arg, struct cmd_unit *cmd, int *pgid) {
int pid;
int pipe_fds[2];
if (cmd->next) {
// Create pipe pair for this -> next communication
int res = pipe(pipe_fds);
if (res != 0) {
return -1;
}
// this stdout = write end
cmd->fds[1] = pipe_fds[1];
// next stdin = read end
cmd->next->fds[0] = pipe_fds[0];
}
if ((pid = fork()) < 0) {
perror("fork()");
return -1;
}
if (pid == 0) {
pid = getpid();
if (*pgid == -1) {
*pgid = pid;
}
setpgid(pid, *pgid);
if (cmd->prev) {
dup2(cmd->fds[0], STDIN_FILENO);
close(cmd->fds[0]);
}
if (cmd->next) {
dup2(cmd->fds[1], STDOUT_FILENO);
close(cmd->fds[1]);
}
if (isatty(STDIN_FILENO)) {
signal(SIGTTOU, SIG_IGN);
tcsetpgrp(STDIN_FILENO, *pgid);
}
exit(fn(arg, cmd));
} else {
cmd->pid = pid;
if (cmd->next) {
// Close write end, it's now handled by child
close(pipe_fds[1]);
}
if (cmd->prev) {
// Close read end, it's now handled by child
close(cmd->fds[0]);
}
return 0;
}
}
static int cmd_exec_binary(struct cmd_unit *cmd, int *pgid) {
char path_path[256];
int res;
if (cmd->args[0][0] == '.' || cmd->args[0][0] == '/') {
if ((res = access(cmd->args[0], X_OK)) != 0) {
perror(cmd->args[0]);
return res;
}
strcpy(path_path, cmd->args[0]);
return cmd_spawn(spawn_binary, path_path, cmd, pgid);
}
const char *pathvar = getenv("PATH");
if (!pathvar || !*pathvar) {
return -1;
}
const char *p = pathvar;
const char *e;
while (1) {
e = strchr(p, ':');
size_t len;
if (!e) {
len = strlen(p);
} else {
len = e - p;
}
strncpy(path_path, p, len);
path_path[len] = '/';
strcpy(path_path + len + 1, cmd->args[0]);
if ((res = access(path_path, X_OK)) == 0) {
return cmd_spawn(spawn_binary, path_path, cmd, pgid);
}
if (!e) {
break;
}
p = e + 1;
}
return -1;
}
static int cmd_unit_exec(struct cmd_unit *cmd, int *pgid) {
int res;
builtin_func_t builtin;
// TODO: don't spawn ALL the commands as separate processes
// e.g. exit is broken and $$ evaluation will be incorrect
// this is just a dirty workaround for "exit"
if (!strcmp(cmd->args[0], "exit")) {
if (cmd->argc > 1) {
exit(atoi(cmd->args[1]));
}
exit(0);
} else {
if ((builtin = builtin_find(cmd->args[0])) != NULL) {
return cmd_spawn(spawn_builtin, builtin, cmd, pgid);
}
}
if ((res = cmd_exec_binary(cmd, pgid)) == 0) {
return 0;
}
printf("sh: command not found: %s\n", cmd->args[0]);
return -1;
}
int eval(char *str) {
char *p;
struct cmd cmd;
int cmd_res, pgid;
while (isspace(*str)) {
++str;
}
if ((p = strchr(str, '#'))) {
*p = 0;
}
if (!*str) {
return 0;
}
if (cmd_parse(str, &cmd) != 0) {
return -1;
}
// Spawn all subprocesses
pgid = -1;
for (struct cmd_unit *u = cmd.first; u; u = u->next) {
u->pid = -1;
cmd_unit_exec(u, &pgid);
}
// Wait for spawned subprocesses to finish
for (struct cmd_unit *u = cmd.first; u; u = u->next) {
if (u->pid != -1) {
if (waitpid(u->pid, &cmd_res, 0) < 0) {
perror("waitpid()");
}
}
}
// Regain control of foreground group
pgid = getpgid(0);
signal(SIGTTOU, SIG_IGN);
tcsetpgrp(STDIN_FILENO, pgid);
return 0;
}
+86
View File
@@ -0,0 +1,86 @@
#include "parse.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
static int cmd_unit_parse(const char *str, struct cmd_unit *unit) {
// Parse until end of string or |
size_t len;
char *word = NULL;
char c;
unit->argc = 0;
// TODO: escapes
// TODO: variable substitution
while ((c = *str++) && c != '|') {
if (isspace(c)) {
if (word) {
word[len] = 0;
unit->args[unit->argc++] = word;
word = NULL;
}
} else {
if (!word) {
word = malloc(256);
len = 0;
}
if (len == 255) {
return -1;
}
word[len++] = c;
}
}
if (word) {
word[len] = 0;
unit->args[unit->argc++] = word;
}
unit->args[unit->argc] = NULL;
return 0;
}
int cmd_parse(const char *str, struct cmd *cmd) {
const char *p = str;
const char *e;
cmd->last = NULL;
while (1) {
while (isspace(*p)) ++p;
if (!*p) {
break;
}
// TODO: escaped/quoted |
// and ||
e = strchr(p, '|');
if (p != e) {
struct cmd_unit *unit = malloc(sizeof(struct cmd_unit));
if (cmd_unit_parse(p, unit) != 0) {
// TODO: cleanup
return -1;
}
unit->next = NULL;
if (cmd->last) {
cmd->last->next = unit;
} else {
cmd->first = unit;
}
unit->prev = cmd->last;
cmd->last = unit;
}
if (!e) {
break;
}
p = e + 1;
}
return 0;
}
+161
View File
@@ -0,0 +1,161 @@
//#include <sys/select.h>
#include <unistd.h>
#include <stdio.h>
//#define KEY_UP (256)
//#define KEY_DOWN (257)
//#define KEY_RIGHT (258)
//#define KEY_LEFT (259)
//
//// With support for escape sequences
//static int getch_del(void) {
// struct timeval tv = {
// .tv_sec = 0,
// .tv_usec = 500000
// };
// fd_set fds;
// FD_ZERO(&fds);
// FD_SET(STDIN_FILENO, &fds);
// int res;
//
// if ((res = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv)) < 0) {
// return res;
// }
//
// if (FD_ISSET(STDIN_FILENO, &fds)) {
// res = 0;
// if (read(STDIN_FILENO, &res, 1) != 1) {
// return -1;
// }
// return res;
// } else {
// return -1;
// }
//}
//
//static int escape_read(void) {
// // Maybe [
// int c = getch_del();
//
// if (c < 0) {
// return '\033';
// }
//
// if (c != '[') {
// return -1;
// }
//
// c = getch_del();
//
// switch (c) {
// case 'A':
// return KEY_UP;
// case 'B':
// return KEY_DOWN;
// case 'C':
// return KEY_RIGHT;
// case 'D':
// return KEY_LEFT;
// default:
// return -1;
// }
//}
//
//static int getch(void) {
// char c;
//
// if (read(STDIN_FILENO, &c, 1) != 1) {
// return -1;
// }
//
// if (c == '\033') {
// int r = escape_read();
//
// if (r > 0) {
// return r;
// }
// }
//
// return c;
//}
int readline(char *buf, size_t lim) {
// TODO: rewrite this for new kernel line discipline to disable ECHO/ICANON
ssize_t len = read(STDIN_FILENO, buf, lim);
// Just strip newline
if (len <= 0) {
return -1;
}
if (buf[len - 1] == '\n') {
buf[len - 1] = 0;
}
//printf("Got len = %ld\n", len);
//while (1);
//int len = 0;
//int cur = 0;
//int chr;
//while (1) {
// if ((chr = getch()) <= 0) {
// return -1;
// }
// if (len == lim) {
// printf("Input line is too long\n");
// return -1;
// }
// switch (chr) {
// case KEY_LEFT:
// if (cur) {
// puts2("\033[D");
// --cur;
// }
// break;
// case KEY_RIGHT:
// if (cur < len) {
// puts2("\033[C");
// ++cur;
// }
// break;
// }
// if (chr == '\n') {
// putchar(chr);
// buf[len] = 0;
// break;
// } else if (chr == '\b') {
// if (cur) {
// --cur;
// for (int i = cur; i < len - 1; ++i) {
// buf[i] = buf[i + 1];
// }
// puts2("\033[D\033[s");
// --len;
// for (int i = cur; i < len; ++i) {
// putchar(buf[i]);
// }
// puts2(" \033[u");
// }
// } else if (chr >= ' ' && chr < 255) {
// putchar(chr);
// puts2("\033[s");
// for (int i = cur; i < len; ++i) {
// putchar(buf[i]);
// }
// puts2("\033[u");
// for (int i = len; i > cur; --i) {
// buf[i] = buf[i - 1];
// }
// buf[cur++] = chr;
// ++len;
// }
//}
return len;
}
+146
View File
@@ -0,0 +1,146 @@
#include <sys/utsname.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <pwd.h>
#include "readline.h"
#include "config.h"
#include "cmd.h"
// Data needed for prompt
static char p_hostname[64];
static char p_username[64];
static char p_cwd[256];
static uid_t p_uid;
//static gid_t p_gid;
static void update_prompt(void) {
struct utsname _u;
struct passwd _p;
struct passwd *p;
char pwbuf[256];
p_uid = getuid();
if (getcwd(p_cwd, sizeof(p_cwd)) == NULL) {
perror("getcwd()");
p_cwd[0] = 0;
}
if (uname(&_u) != 0) {
perror("uname()");
} else {
strcpy(p_hostname, _u.nodename);
}
if (getpwuid_r(p_uid, &_p, pwbuf, sizeof(pwbuf), &p) != 0) {
perror("getpwuid_r()");
p_username[0] = 0;
} else {
strcpy(p_username, p->pw_name);
}
}
static void display_prompt(void) {
const char *p = PS1;
while (*p) {
if (*p == '%') {
switch (*++p) {
case '#':
putchar(p_uid == 0 ? '#' : '$');
break;
case 'h':
fputs(p_hostname, stdout);
break;
case 'u':
fputs(p_username, stdout);
break;
case 'd':
fputs(p_cwd, stdout);
break;
default:
putchar('%');
putchar(*p);
break;
}
} else {
putchar(*p);
}
++p;
}
fflush(stdout);
}
static void signal_handle(int signum) {
switch (signum) {
case SIGINT:
printf("\n");
update_prompt();
display_prompt();
break;
default:
printf("\nUnhandled signal: %d\n", signum);
break;
}
}
int main(int argc, char **argv) {
int fd = STDIN_FILENO;
char linebuf[256];
int res;
signal(SIGINT, signal_handle);
if (argc == 2) {
fd = open(argv[1], O_RDONLY, 0);
if (fd < 0) {
perror(argv[1]);
return -1;
}
} else if (argc > 2) {
printf("usage: sh [filename]\n");
return -1;
}
// TODO: source /etc/profile?
setenv("PATH", "/sbin:/bin", 0);
if (!isatty(fd)) {
while (1) {
// TODO: use fgets here
//if (gets_safe(fd, linebuf, sizeof(linebuf)) < 0) {
// break;
//}
eval(linebuf);
}
if (fd != STDIN_FILENO) {
close(fd);
}
return 0;
}
while (1) {
update_prompt();
display_prompt();
if (readline(linebuf, sizeof(linebuf)) < 0) {
break;
}
if ((res = eval(linebuf)) != 0) {
printf(COLOR_RED "Status: %d" COLOR_RESET "\n", res);
}
}
return 0;
}