From 6fa747864efcb84863d09a9a937315e748a9f040 Mon Sep 17 00:00:00 2001 From: Derek Stevens Date: Mon, 22 Jan 2024 00:50:23 -0700 Subject: [PATCH] reinit; VM and 9p core mostly done --- .gitignore | 2 + build | 7 +++ client.c | 35 ++++++++++++++ dat.h | 102 ++++++++++++++++++++++++++++++++++++++++ fns.h | 20 ++++++++ kuro.c | 75 +++++++++++++++++++++++++++++ mkfile | 24 ++++++++++ node.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++ nodetable.c | 42 +++++++++++++++++ srv.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++ util.c | 20 ++++++++ 11 files changed, 581 insertions(+) create mode 100644 .gitignore create mode 100755 build create mode 100644 client.c create mode 100644 dat.h create mode 100644 fns.h create mode 100644 kuro.c create mode 100644 mkfile create mode 100644 node.c create mode 100644 nodetable.c create mode 100644 srv.c create mode 100644 util.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f005011 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +kuro +kurosrvr diff --git a/build b/build new file mode 100755 index 0000000..02a02ff --- /dev/null +++ b/build @@ -0,0 +1,7 @@ +#!/bin/rc + +mk + +mv 6.out kurosrvr +cp kurosrvr kuro +rm *.6 diff --git a/client.c b/client.c new file mode 100644 index 0000000..f7e71af --- /dev/null +++ b/client.c @@ -0,0 +1,35 @@ +#include "dat.h" +#include "fns.h" + +static char mtpt[256] = {0}; + +void kuro9p_set_mtpt(char* path) { + strcpy(mtpt, path); +} + +void kuro9p_write(char* path, char* data, int len) { + int fd; + char fullpath[256]; + strcpy(fullpath, mtpt); + strcat(fullpath, path); + + fd = open(fullpath, OWRITE); + if (fd >= 0) { + write(fd, data, len); + close(fd); + } +} + +char* kuro9p_read(char* path, char* buf, int len) { + int fd; + char fullpath[256]; + strcpy(fullpath, mtpt); + strcat(fullpath, path); + + fd = open(fullpath, OREAD); + if (fd >= 0) { + read(fd, buf, len); + close(fd); + } + return buf; +} \ No newline at end of file diff --git a/dat.h b/dat.h new file mode 100644 index 0000000..d28119f --- /dev/null +++ b/dat.h @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include +#include +#include + +typedef enum { + CTL = 1 +} FileType; + +typedef enum { + KURO_RUNS = 0, + KURO_QUITS +} WindowStatus; + +typedef enum { + PORT_MOUSE, + PORT_RESIZE, + PORT_KBD, + PORT_STATUS, + TOTAL_PORTS +} Port; + +typedef enum { + INIT, + OPEN_FILE, + SAVE_FILE, + MOUSE, + KEYBOARD, + SCROLL, + CURSOR, + SELECT, + INSERT, + DELETE, + CUT, + COPY, + PASTE, + EXEC, + PLUMB, + INDICATE, + SPLIT, + PARTITION, + TOTAL_OPCODES +} Opcode; + +typedef enum { + TAGF, + BODYF, + SHELLF, + TOTAL_SUBF +} SubFrame; + +typedef struct Aux { + FileType type; + char* data; + int count; +} Aux; + +typedef struct { + Opcode opcode; + void* data; +} Instruction; + +typedef void (*Handler)(void*, void*); + +typedef struct { + uvlong id; + WindowStatus status; + Image* img; + Screen* screen; + char filepath[512]; + Rune* tag; + uint tag_len; + Rune* body; + uint body_len; +} KuroMemory; + +typedef struct { + uvlong id; + int fd; + Channel* cpu; /* chan(Instruction) */ + Handler* handlers; + KuroMemory* memory; + Channel* status; /* chan(WidowStatus) */ +} Node; + +typedef struct NodeRef NodeRef; + +struct NodeRef { + int fd[2]; + uvlong id; + NodeRef* next; +}; + +typedef struct { + NodeRef* data[256]; +} NodeTable; diff --git a/fns.h b/fns.h new file mode 100644 index 0000000..7a25f60 --- /dev/null +++ b/fns.h @@ -0,0 +1,20 @@ +int strequ(char* s1, char* s2); +char* strcsw(char* s, char a, char b); +char* uvlong_to_hex(uvlong input, char* buf); + +void start_9p(char* mtpt); + +void kuro9p_set_mtpt(char* mtpt); +void kuro9p_write(char* path, char* data, int len); +char* kuro9p_read(char* path, char* buf, int len); + +void nodetbl_add(NodeTable* self, NodeRef* node); +void nodetbl_del(NodeTable* self, uvlong id); + +void node_setup(Node* self, uvlong id, int fd, Handler* handlers, KuroMemory* memory); +void node_execute(Node* self); +void node_loop(void* data); +Node* create_node(uvlong id, int fd, int new, char* filename); +void supervise_node(Node* self); +void node_cleanup(Node* self); +void memory_cleanup(KuroMemory* self); \ No newline at end of file diff --git a/kuro.c b/kuro.c new file mode 100644 index 0000000..0a700f0 --- /dev/null +++ b/kuro.c @@ -0,0 +1,75 @@ +#include "dat.h" +#include "fns.h" + +static char* mtpt; + +int threadmaybackground(void) { + return 1; +} + +void threadmain(int argc, char **argv) +{ + /* we want to strip any path elements off the executable name */ + char exe_name[256] = {0}; + char* exe_base; + + uvlong node_id = 0; + int client_fd = -1; + int newwin = 0; + + char client_9p_str[256] = {0}; + + strcpy(exe_name, argv[0]); + exe_base = utfrrune(exe_name, '/'); + if (exe_base) { + exe_base++; + } else { + exe_base = exe_name; + } + + /* if we are the server, serve the filetree on 9p */ + + if (strequ(exe_base, "kurosrvr")) { + if (argc > 1) { + start_9p(argv[1]); + } else { + sysfatal("usage: kurosrvr mtpt"); + } + } else if (strequ(exe_base, "kuro")) { + mtpt = getenv("KURO_MTPT"); + if (mtpt == nil) { + sysfatal("KURO_MTPT not set"); + } + + kuro9p_set_mtpt(mtpt); + print("KURO_MTPT=%s\n", mtpt); + + ARGBEGIN{ + case 'n': + newwin=1; + break; + case 'i': + sscanf(ARGF(), "%d", &node_id); + break; + case 'p': + sscanf(ARGF(), "%d", &client_fd); + break; + }ARGEND + + strcpy(client_9p_str, "new"); + if (argv[0]) { + strcat(client_9p_str, " "); + strcat(client_9p_str, argv[0]); + } + + if (node_id > 0 && client_fd >= 0) { + supervise_node(create_node(node_id, client_fd, newwin, argv[0])); + } else { + /* if node_id or client_id are missing, ask the server to fork us with them, and forward the filename */ + print("9pstr=%s\n", client_9p_str); + kuro9p_write("/ctl", client_9p_str, strlen(client_9p_str)); + } + } else { + sysfatal("invoke as kurosrvr to start the background service or kuro to start an application window"); + } +} diff --git a/mkfile b/mkfile new file mode 100644 index 0000000..9109813 --- /dev/null +++ b/mkfile @@ -0,0 +1,24 @@ +id = id; + self->fd = fd; + self->handlers = handlers; + self->memory = memory; + self->cpu = chancreate(sizeof(Instruction), 0); + self->status = chancreate(sizeof(WindowStatus), 0); + } +} +void node_loop(void* data) { + Node* self = (Node*)data; + Instruction x; + + for (;;) { + recv(self->cpu, &x); + if (self->handlers[x.opcode]) { + (*(self->handlers[x.opcode]))(self, x.data); + switch(self->memory->status) { + case KURO_QUITS: + nbsend(self->status, 0); + threadexits(0); + default: + break; + } + flushimage(self->memory->screen->display, 1); + } + } +} + +void node_execute(Node* self) { + threadcreate(node_loop, self, 1024); +} + +Node* create_node(uvlong id, int fd, int new, char* filename) { + + if (new) { + newwindow("-dx 600 -dy 600"); + } + initdraw(nil, nil, "kuro"); + + KuroMemory* self = (KuroMemory*)malloc(sizeof(KuroMemory)); + Node* node = (Node*)malloc(sizeof(Node)); + Handler* handlers = (Handler*)malloc(TOTAL_OPCODES * sizeof(Handler*)); + + node_setup(node, id, fd, handlers, self); + + self->img = screen; + self->screen = _screen; + + strcpy(self->filepath, filename); + + /* node_execute runs the node on a separate thread */ + node_execute(node); + + return node; +} + +void supervise_node(Node* self) { + + Mousectl* mctl; + Keyboardctl* kctl; + Mouse mouse; + int resize[2]; + WindowStatus status; + Rune kbd; + + if ((mctl = initmouse(nil, screen)) == nil) { + sysfatal("couldn't initialize mctl"); + } + if ((kctl = initkeyboard(nil)) == nil) { + sysfatal("couldn't initialize kctl"); + } + + Alt alts[TOTAL_PORTS + 1] = { + {mctl->c, &mouse, CHANRCV}, + {mctl->resizec, resize, CHANRCV}, + {kctl->c, &kbd, CHANRCV}, + {self->status, &status, CHANRCV}, + {nil, nil, CHANEND} + }; + + for (;;) { + switch (alt(alts)) { + case PORT_MOUSE: + print("got a mouse event"); + break; + case PORT_RESIZE: + lockdisplay(display); + if (getwindow(display, Refnone) < 0) + sysfatal("couldn't resize"); + unlockdisplay(display); + print("got a resize event"); + break; + case PORT_KBD: + print("got a keypress"); + break; + case PORT_STATUS: + switch(status) { + case KURO_QUITS: + print("VM died - let's clean up the data"); + goto cleanup; + default: + break; + } + break; + } + } +cleanup: + node_cleanup(self); +} + +void node_cleanup(Node* self) { + if (self) { + memory_cleanup(self->memory); + chanfree(self->cpu); + chanfree(self->status); + /* TODO: send message on self->fd to tell the server to remove us from NodeTable and filetree */ + /* handlers array is shared between all the nodes */ + free(self); + } +} + +void memory_cleanup(KuroMemory* self) { + if (self) { + /* do we need to free the img and screen? */ + } +} \ No newline at end of file diff --git a/nodetable.c b/nodetable.c new file mode 100644 index 0000000..771f0b3 --- /dev/null +++ b/nodetable.c @@ -0,0 +1,42 @@ +#include "dat.h" +#include "fns.h" + +void nodetbl_add(NodeTable* self, NodeRef* node) { + int i; + NodeRef* n; + if (self && node) { + i = (int)(node->id%256); + n = self->data[i]; + if (n && n->next) { + n = n->next; + } + if (n) { + n->next = node; + } else { + n = node; + } + } +} + +void nodetbl_del(NodeTable* self, uvlong id) { + NodeRef* n; + NodeRef* nprev; + + if (self) { + for(int i = 0; i <= 255; i++) { + n = self->data[i]; + + while (n && n->id != id && n->next) { + nprev = n; + n = n->next; + } + if (n->id == id) { + if (nprev) { + nprev->next = n->next; + } + free(n); + break; + } + } + } +} \ No newline at end of file diff --git a/srv.c b/srv.c new file mode 100644 index 0000000..742ba6e --- /dev/null +++ b/srv.c @@ -0,0 +1,122 @@ +#include "dat.h" +#include "fns.h" + +static NodeTable nodes; +static QLock id_lock; + +static uvlong get_next_id(void) { + static uvlong id = 0; + uvlong next; + qlock(&id_lock); + next = ++id; + qunlock(&id_lock); + return next; +} + +Aux* create_aux(FileType t) { + Aux* self = (Aux*)malloc(sizeof(Aux)); + self->type = t; + self->data = nil; + self->count = 0; + return self; +} + +void fs_destroy_file(File* f) { + Aux* a = (Aux*)f->aux; + if(a && a->data) { + free(a->data); + } + if (a) { + free (a); + } +} + +void kuro_read(Req* r) { + Aux* a = (Aux*)r->fid->file->aux; + switch (a->type) { + case CTL: + default: + respond(r, nil); + break; + } +} + +void new_window(char* filename, int new) { + int rw_fd[2]; + char client_fd[32] = {0}; + char client_id[32] = {0}; + uvlong id; + NodeRef* noderef; + + pipe(rw_fd); + id = get_next_id(); + + noderef = (NodeRef*)malloc(sizeof(NodeRef)); + noderef->id = id; + noderef->fd[0] = rw_fd[0]; + noderef->fd[1] = rw_fd[1]; + nodetbl_add(&nodes, noderef); + + sprintf(client_fd, "%d", rw_fd[1]); + sprintf(client_id, "%d", id); + + rfork(RFNAMEG); + char* a[] = { "kuro", "-p", client_fd, "-i", client_id, new ? "-n" : filename, new ? filename : 0, 0 }; + exec("./kuro", a); +} + +void write_ctl(Req* r) { + char cmd[16] = {0}; + char* c = r->ifcall.data; + int i; + + for (i = 0; i < r->ifcall.count && *c != ' ' && *c != '\n'; i++) { + strncat(cmd, c, 1); + c++; + } + + if (*c == ' ') { + c++; + } + + /* maybe don't do this... */ + strcsw(c, '\n', 0); + + /* diagnostics for now */ + print("cmd: %s\n", cmd); + print("arg: %s\n", c); + + if (strequ(cmd, "new")) { + new_window(c, 1); + respond(r, nil); + } else { + print("unknown command...\n"); + respond(r, nil); + } +} + +void kuro_write(Req* r) { + Aux* a = (Aux*)r->fid->file->aux; + switch (a->type) { + case CTL: + write_ctl(r); + break; + default: + respond(r, nil); + break; + } +} + +void start_9p(char* mtpt) { + if (mtpt) { + putenv("KURO_MTPT", mtpt); + Srv srv = { .read = kuro_read, .write = kuro_write }; + Tree* tree = alloctree(nil, nil, DMDIR | 0777, fs_destroy_file); + srv.tree = tree; + closefile(createfile(tree->root, "ctl", nil, DMAPPEND | 0600, create_aux(CTL))); + closefile(createfile(tree->root, "nodes", nil, DMDIR | 0600, nil)); + closefile(tree->root); + /* TODO: figure out how to kill the server cleanly so we don't need MREPL */ + threadpostmountsrv(&srv, nil, mtpt, MREPL | MCREATE); + } +} diff --git a/util.c b/util.c new file mode 100644 index 0000000..0db6974 --- /dev/null +++ b/util.c @@ -0,0 +1,20 @@ +#include "dat.h" +#include "fns.h" + +int strequ(char* s1, char* s2) { + return strcmp(s1, s2) == 0; +} + +char* strcsw(char* s, char a, char b) { + int i = 0; + char c; + while ((c = s[i])) { + s[i++] = c == a ? b : c; + } + return s; +} + +char* uvlong_to_hex(uvlong input, char* buf) { + sprintf(buf, "%08%x\0", input); + return buf; +} \ No newline at end of file