From c40606cf686962aff1fdecadbf393907160c6eb0 Mon Sep 17 00:00:00 2001 From: mbays Date: Sat, 6 Mar 2021 00:00:00 +0000 Subject: [PATCH] play with child --- main.c | 81 +++++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/main.c b/main.c index 4dada4f..f731430 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -75,6 +77,30 @@ bool write_all(int fd, const char* buf, int n) return true; } +/* Write anything written timelily on `in` to `out`, converting \n to \r\n. + * Return false on read error, else true. */ +static bool stream_text(int in, int out) { + char buf[256]; + struct pollfd pfd = { in, POLLIN , 0 }; + + poll(&pfd, 1, 5000); + while (pfd.revents & POLLIN) { + const int r = read(in, buf, 256 - 1); + if (r < 0) return false; + buf[r] = 0; + + const char *b = buf; + while (*b) { + if (*b == '\n') write(out, "\r\n", 2); + else write(out, b, 1); + ++b; + } + + poll(&pfd, 1, 200); + } + return true; +} + void respond(void *object, const Request_Info *request_info, int socket) { State *state = (State *)object; @@ -87,6 +113,7 @@ void respond(void *object, const Request_Info *request_info, int socket) } Child *child = NULL, *slot = NULL; + bool spawned = false; /* Find child with this cert hash, or spawn new. * For simplicity, we use a static array of children rather than @@ -107,7 +134,7 @@ void respond(void *object, const Request_Info *request_info, int socket) if (child == NULL) { if (slot == NULL) { if (state->num_children == MAX_CHILDREN) { - put("40 Too many children!"); + put("40 Too many children!\r\n"); // TODO: consider infanticide at this point? return; } @@ -116,44 +143,48 @@ void respond(void *object, const Request_Info *request_info, int socket) child = slot; if (!spawn(state->command, state->args, child, socket)) { - put("40 Spawn failure."); + put("40 Spawn failure.\r\n"); return; } child->exists = true; strncpy(child->owner, request_info->tls_client_hash, 32); + + spawned = true; } - // TODO: process magic escape codes in query + const char *q = request_info->query_string_decoded; + if (*q == '!') { + ++q; + if (*q == '?') { + put("10\r\n"); + return; + } else if (*q != '!') { + put("40 Unknown gemrepl meta-command (use '!!' for a literal '!')\r\n"); + } + } - const int qlen = strlen(request_info->query_string_decoded); - if (qlen > 0) { - // FIXME: handle sigpipe - if (write(child->in, request_info->query_string_decoded, qlen) < 0 - || write(child->in, "\n", 1) < 0) { - put("40 Error when writing to child"); + const int qlen = strlen(q); + if (!spawned || qlen > 0) { + signal(SIGPIPE, SIG_IGN); + bool err = (write(child->in, q, qlen) < 0 + || write(child->in, "\n", 1) < 0); + signal(SIGPIPE, SIG_DFL); + if (err) { + put("40 Error when writing to child\r\n"); + // TODO: probably this means the child died. + // Should we already respawn? return; } - // TODO: when to write a bare newline? } put("20 text/gemini\r\n"); - char buf[1024]; - const int r = read(child->out, buf, 1024 - 1); - if (r < 0) { - put("40 Error reading from child"); - return; + + if (!stream_text(child->out, socket)) { + put("[gemrepl: error when reading from child]\r\n"); } - buf[r] = 0; - put(buf); - put("\r\n"); - - // TODO: - // read for while, streaming response - // be a bit clever about when to close; - // maybe: give it 5s to start producing output, - // read until it's blocked for 0.2s, or has been going for more than 2s. - // use poll() or select() to implement timeouts, I guess. + + put("=> ?!? Input command"); } int main(int argc, char **argv) -- 2.11.4.GIT