X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuhttpd.git;a=blobdiff_plain;f=proc.c;h=5be6232e79849843ef6d1422af3a29a3a683aee0;hp=ddd190968368822ce1432bc51bf8280ba5b7e1af;hb=fae8886f8aa5d996c8de607abea8b677216549b5;hpb=387e33a6edc0cbddd3c262a107e3b9026cd55872 diff --git a/proc.c b/proc.c index ddd1909..5be6232 100644 --- a/proc.c +++ b/proc.c @@ -1,38 +1,39 @@ /* * uhttpd - Tiny single-threaded httpd * - * Copyright (C) 2010-2012 Jo-Philipp Wich - * Copyright (C) 2012 Felix Fietkau + * Copyright (C) 2010-2013 Jo-Philipp Wich + * Copyright (C) 2013 Felix Fietkau * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include "uhttpd.h" #define __headers \ - __header(accept) \ - __header(accept_charset) \ - __header(accept_encoding) \ - __header(accept_language) \ - __header(authorization) \ - __header(connection) \ - __header(cookie) \ - __header(host) \ - __header(referer) \ - __header(user_agent) \ - __header(content_type) \ - __header(content_length) + __header(accept, accept) \ + __header(accept_charset, accept-charset) \ + __header(accept_encoding, accept-encoding) \ + __header(accept_language, accept-language) \ + __header(authorization, authorization) \ + __header(connection, connection) \ + __header(cookie, cookie) \ + __header(host, host) \ + __header(referer, referer) \ + __header(user_agent, user-agent) \ + __header(content_type, content-type) \ + __header(content_length, content-length) #undef __header #define __header __enum_header @@ -81,10 +82,20 @@ enum extra_vars { VAR_PATH_INFO, VAR_USER, VAR_REDIRECT, + VAR_SERVER_NAME, + VAR_SERVER_ADDR, + VAR_SERVER_PORT, + VAR_REMOTE_NAME, + VAR_REMOTE_ADDR, + VAR_REMOTE_PORT, __VAR_MAX, }; +static char local_addr[INET6_ADDRSTRLEN], remote_addr[INET6_ADDRSTRLEN]; +static char local_port[6], remote_port[6]; +static char redirect_status[4]; + static struct env_var extra_vars[] = { [_VAR_GW] = { "GATEWAY_INTERFACE", "CGI/1.1" }, [_VAR_SOFTWARE] = { "SERVER_SOFTWARE", "uhttpd" }, @@ -97,7 +108,13 @@ static struct env_var extra_vars[] = { [VAR_METHOD] = { "REQUEST_METHOD" }, [VAR_PATH_INFO] = { "PATH_INFO" }, [VAR_USER] = { "REMOTE_USER" }, - [VAR_REDIRECT] = { "REDIRECT_STATUS" }, + [VAR_REDIRECT] = { "REDIRECT_STATUS", redirect_status }, + [VAR_SERVER_NAME] = { "SERVER_NAME", local_addr }, + [VAR_SERVER_ADDR] = { "SERVER_ADDR", local_addr }, + [VAR_SERVER_PORT] = { "SERVER_PORT", local_port }, + [VAR_REMOTE_NAME] = { "REMOTE_HOST", remote_addr }, + [VAR_REMOTE_ADDR] = { "REMOTE_ADDR", remote_addr }, + [VAR_REMOTE_PORT] = { "REMOTE_PORT", remote_port }, }; struct env_var *uh_get_process_vars(struct client *cl, struct path_info *pi) @@ -106,10 +123,11 @@ struct env_var *uh_get_process_vars(struct client *cl, struct path_info *pi) struct blob_attr *data = cl->hdr.head; struct env_var *vars = (void *) uh_buf; struct blob_attr *tb[__HDR_MAX]; - static char buf[4]; + const char *url; int len; int i; + url = blobmsg_data(blob_data(cl->hdr.head)); len = ARRAY_SIZE(proc_header_env); len += ARRAY_SIZE(extra_vars); len *= sizeof(struct env_var); @@ -120,14 +138,18 @@ struct env_var *uh_get_process_vars(struct client *cl, struct path_info *pi) extra_vars[VAR_SCRIPT_FILE].value = pi->phys; extra_vars[VAR_DOCROOT].value = pi->root; extra_vars[VAR_QUERY].value = pi->query ? pi->query : ""; - extra_vars[VAR_REQUEST].value = req->url; + extra_vars[VAR_REQUEST].value = url; extra_vars[VAR_PROTO].value = http_versions[req->version]; extra_vars[VAR_METHOD].value = http_methods[req->method]; extra_vars[VAR_PATH_INFO].value = pi->info; extra_vars[VAR_USER].value = req->realm ? req->realm->user : NULL; - snprintf(buf, sizeof(buf), "%d", req->redirect_status); - extra_vars[VAR_REDIRECT].value = buf; + snprintf(redirect_status, sizeof(redirect_status), + "%d", req->redirect_status); + inet_ntop(cl->srv_addr.family, &cl->srv_addr.in, local_addr, sizeof(local_addr)); + snprintf(local_port, sizeof(local_port), "%d", cl->srv_addr.port); + inet_ntop(cl->peer_addr.family, &cl->peer_addr.in, remote_addr, sizeof(remote_addr)); + snprintf(remote_port, sizeof(remote_port), "%d", cl->peer_addr.port); blobmsg_parse(hdr_policy, __HDR_MAX, tb, blob_data(data), blob_len(data)); for (i = 0; i < ARRAY_SIZE(proc_header_env); i++) { @@ -148,7 +170,11 @@ struct env_var *uh_get_process_vars(struct client *cl, struct path_info *pi) static void proc_close_fds(struct client *cl) { - close(cl->dispatch.proc.r.sfd.fd.fd); + struct dispatch_proc *p = &cl->dispatch.proc; + + close(p->r.sfd.fd.fd); + if (p->wrfd.fd >= 0) + close(p->wrfd.fd); } static void proc_handle_close(struct relay *r, int ret) @@ -178,6 +204,7 @@ static void proc_handle_header(struct relay *r, const char *name, const char *va buf[3] = 0; snprintf(status_buf, sizeof(status_buf), "%s", sep + 1); cl->dispatch.proc.status_msg = status_buf; + cl->dispatch.proc.status_code = atoi(buf); return; } @@ -187,9 +214,11 @@ static void proc_handle_header(struct relay *r, const char *name, const char *va static void proc_handle_header_end(struct relay *r) { struct client *cl = r->cl; + struct dispatch_proc *p = &cl->dispatch.proc; struct blob_attr *cur; int rem; + uloop_timeout_cancel(&p->timeout); uh_http_header(cl, cl->dispatch.proc.status_code, cl->dispatch.proc.status_msg); blob_for_each_attr(cur, cl->dispatch.proc.hdr.head, rem) ustream_printf(cl->us, "%s: %s\r\n", blobmsg_name(cur), blobmsg_data(cur)); @@ -197,45 +226,156 @@ static void proc_handle_header_end(struct relay *r) ustream_printf(cl->us, "\r\n"); } +static void proc_write_close(struct client *cl) +{ + 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_free(struct client *cl) { - uh_relay_free(&cl->dispatch.proc.r); + struct dispatch_proc *p = &cl->dispatch.proc; + + uloop_timeout_cancel(&p->timeout); + blob_buf_free(&p->hdr); + proc_write_close(cl); + uh_relay_free(&p->r); +} + +static void proc_write_cb(struct uloop_fd *fd, unsigned int events) +{ + struct client *cl = container_of(fd, struct client, dispatch.proc.wrfd); + + client_poll_post_data(cl); +} + +static void proc_relay_write_cb(struct client *cl) +{ + struct dispatch_proc *p = &cl->dispatch.proc; + + if (ustream_pending_data(cl->us, true)) + return; + + ustream_set_read_blocked(&p->r.sfd.stream, false); + p->r.sfd.stream.notify_read(&p->r.sfd.stream, 0); +} + +static int proc_data_send(struct client *cl, const char *data, int len) +{ + struct dispatch_proc *p = &cl->dispatch.proc; + int retlen = 0; + int ret; + + while (len) { + ret = write(p->wrfd.fd, data, len); + + if (ret < 0) { + if (errno == EINTR) + continue; + + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + + /* consume all data */ + ret = len; + } + + if (!ret) + break; + + retlen += ret; + len -= ret; + data += ret; + } + + if (len) + uloop_fd_add(&p->wrfd, ULOOP_WRITE); + else + uloop_fd_delete(&p->wrfd); + + return retlen; +} + +static void proc_timeout_cb(struct uloop_timeout *timeout) +{ + struct dispatch_proc *proc = container_of(timeout, struct dispatch_proc, timeout); + struct client *cl = container_of(proc, struct client, dispatch.proc); + + uh_relay_kill(cl, &proc->r); } -bool uh_create_process(struct client *cl, struct path_info *pi, - void (*cb)(struct client *cl, struct path_info *pi, int fd)) +bool uh_create_process(struct client *cl, struct path_info *pi, char *url, + void (*cb)(struct client *cl, struct path_info *pi, char *url)) { - int fds[2]; + struct dispatch *d = &cl->dispatch; + struct dispatch_proc *proc = &d->proc; + int rfd[2], wfd[2]; int pid; - blob_buf_init(&cl->dispatch.proc.hdr, 0); - cl->dispatch.proc.status_code = 200; - cl->dispatch.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, url); exit(0); } - close(fds[1]); - uh_relay_open(cl, &cl->dispatch.proc.r, fds[0], pid); - cl->dispatch.free = proc_free; - cl->dispatch.close_fds = proc_close_fds; - cl->dispatch.proc.r.header_cb = proc_handle_header; - cl->dispatch.proc.r.header_end = proc_handle_header_end; - cl->dispatch.proc.r.close = proc_handle_close; + 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_write_close; + d->write_cb = proc_relay_write_cb; + 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; + proc->timeout.cb = proc_timeout_cb; + if (conf.script_timeout > 0) + uloop_timeout_set(&proc->timeout, conf.script_timeout * 1000); return true; + +close_wfd: + close(wfd[0]); + close(wfd[1]); +close_rfd: + close(rfd[0]); + close(rfd[1]); + + return false; }