diff --git a/src/cmd/init/driver/termcook.c b/src/cmd/init/driver/termcook.c index a0da8c3..e0edcf5 100644 --- a/src/cmd/init/driver/termcook.c +++ b/src/cmd/init/driver/termcook.c @@ -1,10 +1,16 @@ #include "driver.h" +#include #include +#include +#include +#include #include #include #include #include #include +#include +#include #include enum tstate { @@ -13,6 +19,19 @@ enum tstate { CSI, }; +enum handles { + Hroot, + Htokill, +}; + +typedef struct Tokill Tokill; +struct Tokill { + Tokill *next; + char path[]; /* NUL-terminated */ +}; +static Tokill *tokill = NULL; +static hid_t stdin_pipe[2]; + static void w_output(hid_t output, const char *buf, size_t len) { size_t pos = 0; while (pos < len) { @@ -22,10 +41,22 @@ static void w_output(hid_t output, const char *buf, size_t len) { } } -static void line_editor(hid_t input, hid_t output) { +static void send_intr(char *intr) { + for (Tokill *it = tokill; it; it = it->next) { + int fd = camellia_open(it->path, OPEN_WRITE); + // TODO remove dead from list + if (fd < 0) continue; + _sys_write(fd, intr, strlen(intr), -1, 0); + close(fd); + } +} + +static void line_editor(void *) { char readbuf[16], linebuf[256]; size_t linepos = 0; enum tstate state = Normal; + hid_t input = 0; + hid_t output = stdin_pipe[1]; for (;;) { int readlen = _sys_read(input, readbuf, sizeof readbuf, -1); if (readlen < 0) { @@ -46,7 +77,11 @@ static void line_editor(hid_t input, hid_t output) { } break; case 3: /* C-c */ - _sys_exit(1); + send_intr(""); + break; + case 0x1c: /* C-\ */ + send_intr("kill"); + break; case 4: /* EOT, C-d */ if (linepos > 0) { w_output(output, linebuf, linepos); @@ -88,23 +123,57 @@ static void line_editor(hid_t input, hid_t output) { } } -void termcook(void) { - hid_t stdin_pipe[2] = {-1, -1}; - if (_sys_pipe(stdin_pipe, 0) < 0) - return; +static void fs(void *) { + const size_t buflen = 1024; + char *buf = malloc(buflen); + int pipefd = stdin_pipe[0]; + if (!buf) err(1, "malloc"); + for (;;) { + struct ufs_request req; + hid_t reqh = ufs_wait(buf, buflen, &req); + int id = (int)req.id; + if (reqh < 0) errx(1, "ufs_wait error"); - if (!fork()) { - /* the caller continues in a child process, - * so it can be killed when the line editor quits */ - _sys_dup(stdin_pipe[0], 0, 0); - close(stdin_pipe[0]); - close(stdin_pipe[1]); - return; + if (req.op == VFSOP_OPEN) { + if (strcmp(buf, "/") == 0) { + _sys_fs_respond(reqh, (void*)Hroot, 0, 0); + } else if (strcmp(buf, "/tokill") == 0) { + _sys_fs_respond(reqh, (void*)Htokill, 0, 0); + } else if (strcmp(buf, "/stdin") == 0) { + _sys_fs_respond(reqh, NULL, pipefd, FSR_DELEGATE); + } else { + _sys_fs_respond(reqh, NULL, -ENOENT, 0); + } + } else if (id == Hroot && (req.op == VFSOP_READ || req.op == VFSOP_GETSIZE)) { + struct dirbuild db; + char *target = req.op == VFSOP_READ ? buf : NULL; + dir_start(&db, req.offset, target, buflen); + dir_append(&db, "stdin"); + _sys_fs_respond(reqh, target, dir_finish(&db), 0); + } else if (id == Htokill && req.op == VFSOP_WRITE) { + // alternatively, pass fd? + Tokill *head = calloc(sizeof(Tokill) + req.len + 1, 1); + if (!head) { + _sys_fs_respond(reqh, NULL, -ENOMEM, 0); + continue; + } + memcpy(head->path, buf, req.len); + head->next = tokill; + tokill = head; + _sys_fs_respond(reqh, NULL, req.len, 0); + } else { + _sys_fs_respond(reqh, NULL, -ENOSYS, 0); + } } - if (!fork()) { - close(stdin_pipe[0]); - line_editor(0, stdin_pipe[1]); - exit(0); - } - exit(_sys_await()); +} + +// TODO turn into a separate binary +void termcook(void) { + if (_sys_pipe(stdin_pipe, 0) < 0) + err(1, "pipe"); + + thread_create(0, fs, NULL); + thread_create(0, line_editor, NULL); + _sys_await(); + exit(1); } diff --git a/src/cmd/init/init.c b/src/cmd/init/init.c index 5c49281..d8cb1f4 100644 --- a/src/cmd/init/init.c +++ b/src/cmd/init/init.c @@ -1,4 +1,5 @@ #include "driver/driver.h" +#include #include #include #include @@ -12,9 +13,9 @@ void redirect(const char *exe, const char *out, const char *in) { if (!fork()) { - static char title[128]; - snprintf(title, sizeof title, "sh >%s", out); - setproctitle(title); + static char buf[128]; + snprintf(buf, sizeof buf, "sh >%s", out); + setproctitle(buf); if (!freopen(out, "a+", stderr)) { fprintf(stdout, "couldn't open %s\n", out); @@ -25,17 +26,44 @@ void redirect(const char *exe, const char *out, const char *in) { if (!freopen(in, "r", stdin)) err(1, "couldn't open %s", in); + // TODO move to /dev/term/ + MOUNT_AT("/") { + fs_dirinject2((const char*[]){ "/term/", NULL }); + } + MOUNT_AT("/term/") { + termcook(); + } + int fd = camellia_open("/term/stdin", OPEN_READ); + if (fd < 0) { + err(1, "open /term/stdin"); + } + _sys_dup(fd, 0, 0); + close(fd); + for (;;) { - if (!fork()) { + int pid = fork(); + if (pid == 0) { + // TODO shadow over /term/tokill, as it's an easy thing to miss when sandboxing const char *argv[] = {exe, NULL}; - termcook(); execv(exe, (void*)argv); fprintf(stderr, "init: couldn't start %s\n", exe); exit(1); + } else { + int len, fd; + fd = camellia_open("/term/tokill", OPEN_WRITE); + if (fd < 0) { + _sys_intr("kill", 4); + err(1, "open /term/tokill"); + } + len = snprintf(buf, sizeof buf, "/proc/%d/intrdown", pid); + _sys_write(fd, buf, len, 0, 0); + len = snprintf(buf, sizeof buf, "/proc/%d/intr", pid); + _sys_write(fd, buf, len, 0, 0); + close(fd); + + _sys_await(); + _sys_sleep(1000); } - _sys_await(); - _sys_intr(NULL, 0); - _sys_sleep(1000); } } } diff --git a/src/cmd/shell/shell.c b/src/cmd/shell/shell.c index b5ad907..724c018 100644 --- a/src/cmd/shell/shell.c +++ b/src/cmd/shell/shell.c @@ -141,6 +141,7 @@ static void run(char *cmd) { int main(int argc, char **argv) { static char buf[256]; FILE *f = stdin; + intr_set(NULL); if (argc > 1) { f = fopen(argv[1], "r");