commit 6fa747864efcb84863d09a9a937315e748a9f040 Author: Derek Stevens Date: Mon Jan 22 00:50:23 2024 -0700 reinit; VM and 9p core mostly done 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