commit - 60ba426e7e5da49c017f306be78446032cdaf1cf
commit + aff8d1901084bbfd81f4a6335dbed70a02b930fb
blob - 2f786bce62e15bbbebfdde9ea693ba0ef1964fd0
blob + ebc4c6242867481af87a37946813cc77ede1392e
--- ChangeLog
+++ ChangeLog
* gmid.c (url_after_proto): ensure that the requested protocol is
“gemini” and not something else that’s long 6 bytes.
+ * gmid.c (loop): added support for cgi scripts (can handle multiple
+ concurrently)
+
2020-11-06 Omar Polo <op@venera>
* gmid.1: added option to log to a file
blob - 4c258e917794e087d73fc576648c5e1fbf4661bd
blob + 8ab175cccb88092aec202bd08a62cbbc524e091c
--- gmid.c
+++ gmid.c
int state;
int code;
const char *meta;
- int fd;
+ int fd, waiting_on_child;
pid_t child;
- void *buf, *i;
- ssize_t len, off;
+ char sbuf[1024]; /* static buffer */
+ void *buf, *i; /* mmap buffer */
+ ssize_t len, off; /* mmap/static buffer */
int af;
struct in_addr addr;
};
const char *mime(const char*);
int open_file(char*, char*, struct pollfd*, struct client*);
void start_cgi(const char*, const char*, struct pollfd*, struct client*);
+void cgi_setpoll_on_child(struct pollfd*, struct client*);
+void cgi_setpoll_on_client(struct pollfd*, struct client*);
void handle_cgi(struct pollfd*, struct client*);
void send_file(char*, char*, struct pollfd*, struct client*);
void send_dir(char*, struct pollfd*, struct client*);
close(c->fd);
c->fd = p[0];
c->child = pid;
+ mark_nonblock(c->fd);
+ c->state = S_SENDING;
handle_cgi(fds, c);
return;
}
childerr:
dprintf(p[1], "%d internal server error\r\n", TEMP_FAILURE);
close(p[1]);
+
+ /* don't call atexit stuff */
_exit(1);
}
void
-handle_cgi(struct pollfd *fds, struct client *c)
+cgi_setpoll_on_child(struct pollfd *fds, struct client *c)
{
- char buf[1024];
- ssize_t r, todo;
+ int fd;
+ if (c->waiting_on_child)
+ return;
+ c->waiting_on_child = 1;
+
+ fds->events = POLLIN;
+
+ fd = fds->fd;
+ fds->fd = c->fd;
+ c->fd = fd;
+}
+
+void
+cgi_setpoll_on_client(struct pollfd *fds, struct client *c)
+{
+ int fd;
+
+ if (!c->waiting_on_child)
+ return;
+ c->waiting_on_child = 0;
+
+ fd = fds->fd;
+ fds->fd = c->fd;
+ c->fd = fd;
+}
+
+void
+handle_cgi(struct pollfd *fds, struct client *c)
+{
+ ssize_t r;
+
+ /* ensure c->fd is the child and fds->fd the client */
+ cgi_setpoll_on_client(fds, c);
+
while (1) {
- r = read(c->fd, buf, sizeof(buf));
- if (r == -1 || r == 0)
- break;
- todo = r;
- while (todo > 0) {
- switch (r = tls_write(c->ctx, buf, todo)) {
+ if (c->len == 0) {
+ if ((r = read(c->fd, c->sbuf, sizeof(c->sbuf))) == 0)
+ goto end;
+ if (r == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ cgi_setpoll_on_child(fds, c);
+ return;
+ }
+ goto end;
+ }
+ c->len = r;
+ c->off = 0;
+ }
+
+ while (c->len > 0) {
+ switch (r = tls_write(c->ctx, c->sbuf + c->off, c->len)) {
case -1:
goto end;
case TLS_WANT_POLLOUT:
+ fds->events = POLLOUT;
+ return;
+
case TLS_WANT_POLLIN:
- /* evil! */
- continue;
+ fds->events = POLLIN;
+ return;
default:
- todo -= r;
+ c->off += r;
+ c->len -= r;
+ break;
}
}
}
/* fallthrough */
case S_SENDING:
- send_file(NULL, NULL, fds, client);
+ if (client->child != -1)
+ handle_cgi(fds, client);
+ else
+ send_file(NULL, NULL, fds, client);
break;
case S_CLOSING:
err(1, "bad fd %d", fds[i].fd);
if (fds[i].revents & POLLHUP) {
- goodbye(&fds[i], &clients[i]);
- continue;
+ /* fds[i] may be the fd of the stdin
+ * of a cgi script that has exited. */
+ if (!clients[i].waiting_on_child) {
+ goodbye(&fds[i], &clients[i]);
+ continue;
+ }
}
todo--;