From: Felix Fietkau Date: Thu, 3 Jan 2013 21:45:18 +0000 (+0100) Subject: use pipes instead of a socketpair, EOF handling is broken with sockets X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=af526c6c2ede713237048a91aaddfa2ce621dc3c;p=project%2Fuhttpd.git use pipes instead of a socketpair, EOF handling is broken with sockets --- diff --git a/cgi.c b/cgi.c index 02665d8..ad280cd 100644 --- a/cgi.c +++ b/cgi.c @@ -36,14 +36,11 @@ void uh_interpreter_add(const char *ext, const char *path) list_add_tail(&in->list, &interpreters); } -static void cgi_main(struct client *cl, struct path_info *pi, int fd) +static void cgi_main(struct client *cl, struct path_info *pi) { const struct interpreter *ip = pi->ip; struct env_var *var; - dup2(fd, 0); - dup2(fd, 1); - close(fd); clearenv(); setenv("PATH", conf.cgi_path, 1); diff --git a/client.c b/client.c index 43c4af8..7920102 100644 --- a/client.c +++ b/client.c @@ -277,7 +277,8 @@ void client_poll_post_data(struct client *cl) break; if (d->data_send) - d->data_send(cl, buf, cur_len); + cur_len = d->data_send(cl, buf, cur_len); + r->content_length -= cur_len; ustream_consume(cl->us, cur_len); continue; diff --git a/proc.c b/proc.c index 3ebfffd..aa9b8fc 100644 --- a/proc.c +++ b/proc.c @@ -218,98 +218,130 @@ static void proc_handle_header_end(struct relay *r) ustream_printf(cl->us, "\r\n"); } -static void proc_free(struct client *cl) +static void proc_write_close(struct client *cl) { - uh_relay_free(&cl->dispatch.proc.r); + struct dispatch_proc *p = &cl->dispatch.proc; + + if (p->wrfd.fd < 0) + return; + + uloop_fd_delete(&p->wrfd); + close(p->wrfd.fd); + p->wrfd.fd = -1; } -static void proc_write_close(struct client *cl) +static void proc_free(struct client *cl) { - shutdown(cl->dispatch.proc.r.sfd.fd.fd, SHUT_WR); + proc_write_close(cl); + uh_relay_free(&cl->dispatch.proc.r); } -static void proc_relay_write_cb(struct ustream *us, int bytes) +static void proc_write_cb(struct uloop_fd *fd, unsigned int events) { - struct client *cl = container_of(us, struct client, dispatch.proc.r.sfd.stream); - - if (ustream_pending_data(us, true)) - return; + struct client *cl = container_of(fd, struct client, dispatch.proc.wrfd); - cl->dispatch.data_blocked = false; - us->notify_write = NULL; client_poll_post_data(cl); } -static void proc_relay_write_close_cb(struct ustream *us, int bytes) +static int proc_data_send(struct client *cl, const char *data, int len) { - struct client *cl = container_of(us, struct client, dispatch.proc.r.sfd.stream); + struct dispatch_proc *p = &cl->dispatch.proc; + int retlen = 0; + int ret; - if (ustream_pending_data(us, true)) - return; + while (len) { + ret = write(p->wrfd.fd, data, len); - proc_write_close(cl); -} + if (ret < 0) { + if (errno == EINTR) + continue; -static void proc_data_send(struct client *cl, const char *data, int len) -{ - struct ustream *us = &cl->dispatch.proc.r.sfd.stream; + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; - ustream_write(us, data, len, false); - if (ustream_pending_data(us, true)) { - cl->dispatch.data_blocked = true; - us->notify_write = proc_relay_write_cb; - } -} + /* error, no retry */ + len = 0; + break; + } -static void proc_data_done(struct client *cl) -{ - struct ustream *us = &cl->dispatch.proc.r.sfd.stream; + if (!ret) + break; - if (ustream_pending_data(us, true)) { - us->notify_write = proc_relay_write_close_cb; - return; + retlen += ret; + len -= ret; + data += ret; } - proc_write_close(cl); + if (len) + uloop_fd_add(&p->wrfd, ULOOP_WRITE); + else + uloop_fd_delete(&p->wrfd); + + return retlen; } bool uh_create_process(struct client *cl, struct path_info *pi, - void (*cb)(struct client *cl, struct path_info *pi, int fd)) + void (*cb)(struct client *cl, struct path_info *pi)) { struct dispatch *d = &cl->dispatch; - int fds[2]; + struct dispatch_proc *proc = &d->proc; + int rfd[2], wfd[2]; int pid; - blob_buf_init(&cl->dispatch.proc.hdr, 0); - d->proc.status_code = 200; - d->proc.status_msg = "OK"; + blob_buf_init(&proc->hdr, 0); + proc->status_code = 200; + proc->status_msg = "OK"; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) + if (pipe(rfd)) return false; + if (pipe(wfd)) + goto close_rfd; + pid = fork(); - if (pid < 0) { - close(fds[0]); - close(fds[1]); - return false; - } + if (pid < 0) + goto close_wfd; if (!pid) { - close(fds[0]); + close(0); + close(1); + + dup2(rfd[1], 1); + dup2(wfd[0], 0); + + close(rfd[0]); + close(rfd[1]); + close(wfd[0]); + close(wfd[1]); + uh_close_fds(); - cb(cl, pi, fds[1]); + cb(cl, pi); exit(0); } - close(fds[1]); - uh_relay_open(cl, &cl->dispatch.proc.r, fds[0], pid); + close(rfd[1]); + close(wfd[0]); + + proc->wrfd.fd = wfd[1]; + uh_relay_open(cl, &proc->r, rfd[0], pid); + d->free = proc_free; d->close_fds = proc_close_fds; d->data_send = proc_data_send; - d->data_done = proc_data_done; - d->proc.r.header_cb = proc_handle_header; - d->proc.r.header_end = proc_handle_header_end; - d->proc.r.close = proc_handle_close; + d->data_done = proc_write_close; + proc->r.header_cb = proc_handle_header; + proc->r.header_end = proc_handle_header_end; + proc->r.close = proc_handle_close; + proc->wrfd.cb = proc_write_cb; return true; + +close_wfd: + close(wfd[0]); + close(wfd[1]); +close_rfd: + close(rfd[0]); + close(rfd[1]); + + return false; } diff --git a/uhttpd.h b/uhttpd.h index 3cd040d..a9777e8 100644 --- a/uhttpd.h +++ b/uhttpd.h @@ -130,6 +130,14 @@ struct relay { void (*close)(struct relay *r, int ret); }; +struct dispatch_proc { + struct blob_buf hdr; + struct uloop_fd wrfd; + struct relay r; + int status_code; + char *status_msg; +}; + struct dispatch_handler { struct list_head list; @@ -139,7 +147,7 @@ struct dispatch_handler { }; struct dispatch { - void (*data_send)(struct client *cl, const char *data, int len); + int (*data_send)(struct client *cl, const char *data, int len); void (*data_done)(struct client *cl); void (*write_cb)(struct client *cl); void (*close_fds)(struct client *cl); @@ -151,12 +159,7 @@ struct dispatch { struct blob_attr **hdr; int fd; } file; - struct { - struct blob_buf hdr; - struct relay r; - int status_code; - char *status_msg; - } proc; + struct dispatch_proc proc; }; }; @@ -227,6 +230,6 @@ void uh_relay_free(struct relay *r); struct env_var *uh_get_process_vars(struct client *cl, struct path_info *pi); bool uh_create_process(struct client *cl, struct path_info *pi, - void (*cb)(struct client *cl, struct path_info *pi, int fd)); + void (*cb)(struct client *cl, struct path_info *pi)); #endif