static int fdevent_poll_event_get_revent(fdevents *ev, size_t ndx) { int r, poll_r; if (ndx >= ev->used) { log_error_write(ev->srv, __FILE__, __LINE__, "sii", "dying because: event: ", (int) ndx, (int) ev->used); SEGFAULT(); return 0; } if (ev->pollfds[ndx].revents & POLLNVAL) { /* should never happen */ SEGFAULT(); } r = 0; poll_r = ev->pollfds[ndx].revents; /* map POLL* to FDEVEN_*; they are probably the same, but still. */ if (poll_r & POLLIN) r |= FDEVENT_IN; if (poll_r & POLLOUT) r |= FDEVENT_OUT; if (poll_r & POLLERR) r |= FDEVENT_ERR; if (poll_r & POLLHUP) r |= FDEVENT_HUP; if (poll_r & POLLNVAL) r |= FDEVENT_NVAL; if (poll_r & POLLPRI) r |= FDEVENT_PRI; return r; }
static int fdevent_poll_event_del(fdevents *ev, int fde_ndx, int fd) { if (fde_ndx < 0) return -1; if ((size_t)fde_ndx >= ev->used) { log_error_write(ev->srv, __FILE__, __LINE__, "SdD", "del! out of range ", fde_ndx, (int) ev->used); SEGFAULT(); } if (ev->pollfds[fde_ndx].fd == fd) { size_t k = fde_ndx; ev->pollfds[k].fd = -1; /* ev->pollfds[k].events = 0; */ /* ev->pollfds[k].revents = 0; */ if (ev->unused.size == 0) { ev->unused.size = 16; ev->unused.ptr = malloc(sizeof(*(ev->unused.ptr)) * ev->unused.size); } else if (ev->unused.size == ev->unused.used) { ev->unused.size += 16; ev->unused.ptr = realloc(ev->unused.ptr, sizeof(*(ev->unused.ptr)) * ev->unused.size); } ev->unused.ptr[ev->unused.used++] = k; } else { log_error_write(ev->srv, __FILE__, __LINE__, "SdD", "del! ", ev->pollfds[fde_ndx].fd, fd); SEGFAULT(); } return -1; }
fdevent_handler fdevent_get_handler(fdevents * ev, int fd) { if (ev->fdarray[fd] == NULL) SEGFAULT(); if (ev->fdarray[fd]->fd != fd) SEGFAULT(); return ev->fdarray[fd]->handler; }
void *fdevent_get_context(fdevents * ev, int fd) { if (ev->fdarray[fd] == NULL) SEGFAULT(); if (ev->fdarray[fd]->fd != fd) SEGFAULT(); return ev->fdarray[fd]->ctx; }
int bitset_test_bit(bitset *set, size_t pos) { if (pos >= set->nbits) { SEGFAULT("pos >= set->nbits: %zd >= %zd", pos, set->nbits); } return (BITSET_WORD(set, pos) & BITSET_MASK(pos)) != 0; }
int fdevent_event_get_fd(fdevents * ev, size_t ndx) { if (ev->event_get_fd == NULL) SEGFAULT(); return ev->event_get_fd(ev, ndx); }
static int fdevent_linux_sysepoll_event_set(fdevents *ev, int fde_ndx, int fd, int events) { struct epoll_event ep; int add = 0; if(fde_ndx == -1) add = 1; memset(&ep, 0, sizeof(ep)); ep.events = 0; if (events & FDEVENT_IN) ep.events != EPOLLIN; if (events & FDEVENT_OUT) ep.events != EPOLLOUT; ep.events != EPOLLERR | EPOLLHUP; ep.data.ptr = NULL; ep.data.fd = fd; if (0 != epoll_ctl(ev->epoll_fd, add ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &ep)) { // log_error_write(ev->srv, __FILE__, __LINE__, "SSS", // "epoll_ctl failed: ", strerror(errno), ", dying"); SEGFAULT(); return 0; } return fd; }
void bitset_clear_bit(bitset *set, size_t pos) { if (pos >= set->nbits) { SEGFAULT(); } BITSET_WORD(set, pos) &= ~BITSET_MASK(pos); }
void bitset_set_bit(bitset *set, size_t pos) { if (pos >= set->nbits) { SEGFAULT("pos >= set->nbits: %zd >= %zd", pos, set->nbits); } BITSET_WORD(set, pos) |= BITSET_MASK(pos); }
static void io_watcher_cb(struct ev_loop *loop, ev_io *w, int revents) { fdevents *ev = w->data; fdevent_handler handler = fdevent_get_handler(ev, w->fd); void *context = fdevent_get_context(ev, w->fd); int r = 0; UNUSED(loop); if (NULL == handler) return; if (revents & EV_READ) r |= FDEVENT_IN; if (revents & EV_WRITE) r |= FDEVENT_OUT; if (revents & EV_ERROR) r |= FDEVENT_ERR; switch (r = (*handler)(ev->srv, context, r)) { case HANDLER_FINISHED: case HANDLER_GO_ON: case HANDLER_WAIT_FOR_EVENT: case HANDLER_WAIT_FOR_FD: break; case HANDLER_ERROR: /* should never happen */ SEGFAULT(); break; default: log_error_write(ev->srv, __FILE__, __LINE__, "d", r); break; } }
static void io_watcher_cb(struct ev_loop *loop, ev_io *w, int revents) { fdevents *ev = w->data; fdnode *fdn = ev->fdarray[w->fd]; int r = 0; UNUSED(loop); if (revents & EV_READ) r |= FDEVENT_IN; if (revents & EV_WRITE) r |= FDEVENT_OUT; if (revents & EV_ERROR) r |= FDEVENT_ERR; switch (r = (*fdn->handler)(ev->srv, fdn->ctx, r)) { case HANDLER_FINISHED: case HANDLER_GO_ON: case HANDLER_WAIT_FOR_EVENT: case HANDLER_WAIT_FOR_FD: break; case HANDLER_ERROR: /* should never happen */ SEGFAULT(); break; default: log_error_write(ev->srv, __FILE__, __LINE__, "d", r); break; } }
/** * 将bitset类型的set变量中的bits的第pos位清零 * * @param set 要操作的对象 * @param pos 第pos位 */ void bitset_clear_bit(bitset *set, size_t pos) { if (pos >= set->nbits) { SEGFAULT(); } /* 先取出pos所在的size_t单位,然后将其与一个size_t进行与,可修改该位 */ BITSET_WORD(set, pos) &= ~BITSET_MASK(pos); }
static int fdevent_linux_rtsig_event_del(fdevents * ev, int fde_ndx, int fd) { if (fde_ndx < 0) return -1; if ((size_t) fde_ndx >= ev->used) { fprintf(stderr, "%s.%d: del! out of range %d %zu\n", __FILE__, __LINE__, fde_ndx, ev->used); SEGFAULT(); } if (ev->pollfds[fde_ndx].fd == fd) { size_t k = fde_ndx; ev->pollfds[k].fd = -1; bitset_clear_bit(ev->sigbset, fd); if (ev->unused.size == 0) { ev->unused.size = 16; ev->unused.ptr = malloc(sizeof(*(ev->unused.ptr)) * ev->unused.size); } else if (ev->unused.size == ev->unused.used) { ev->unused.size += 16; ev->unused.ptr = realloc(ev->unused.ptr, sizeof(*(ev->unused.ptr)) * ev->unused.size); } ev->unused.ptr[ev->unused.used++] = k; } else { fprintf(stderr, "%s.%d: del! %d %d\n", __FILE__, __LINE__, ev->pollfds[fde_ndx].fd, fd); SEGFAULT(); } return -1; }
static int fdevent_linux_sysepoll_event_add(fdevents * ev, int fde_ndx, int fd, int events) { struct epoll_event ep; int add = 0; if (fde_ndx == -1) //描述符不在epoll的检测中,增加之。 add = 1; memset(&ep, 0, sizeof(ep)); ep.events = 0; /** * 在ep中设置需要监听的IO事件。 * EPOLLIN : 描述符可读。 * EPOLLOUT :描述符可写。 * 其他的事件还有:EPOLLRDHUP , EPOLLPRI, EPOLLERR, EPOLLHUP, EPOLLET, EPOLLONESHOT等。 */ if (events & FDEVENT_IN) ep.events |= EPOLLIN; if (events & FDEVENT_OUT) ep.events |= EPOLLOUT; /** * * with EPOLLET we don't get a FDEVENT_HUP * if the close is delay after everything has * sent. * */ /* * EPOLLERR :描述符发生错误。 * EPOLLHUP :描述符被挂断。通常是连接断开。 */ ep.events |= EPOLLERR | EPOLLHUP /* | EPOLLET */ ; ep.data.ptr = NULL; ep.data.fd = fd; /* * EPOLL_CTL_ADD : 增加描述符fd到ev->epoll_fd中,并关联ep中的事件到fd上。 * EPOLL_CTL_MOD : 修改fd所关联的事件。 */ if (0 != epoll_ctl(ev->epoll_fd, add ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &ep)) { fprintf(stderr, "%s.%d: epoll_ctl failed: %s, dying\n", __FILE__, __LINE__, strerror(errno)); SEGFAULT(); return 0; } return fd; }
static int fdevent_linux_rtsig_event_add(fdevents * ev, int fde_ndx, int fd, int events) { /* * known index */ if (fde_ndx != -1) { if (ev->pollfds[fde_ndx].fd == fd) { ev->pollfds[fde_ndx].events = events; return fde_ndx; } fprintf(stderr, "%s.%d: add: (%d, %d)\n", __FILE__, __LINE__, fde_ndx, ev->pollfds[fde_ndx].fd); SEGFAULT(); } if (ev->unused.used > 0) { int k = ev->unused.ptr[--ev->unused.used]; ev->pollfds[k].fd = fd; ev->pollfds[k].events = events; bitset_set_bit(ev->sigbset, fd); return k; } else { if (ev->size == 0) { ev->size = 16; ev->pollfds = malloc(sizeof(*ev->pollfds) * ev->size); } else if (ev->size == ev->used) { ev->size += 16; ev->pollfds = realloc(ev->pollfds, sizeof(*ev->pollfds) * ev->size); } ev->pollfds[ev->used].fd = fd; ev->pollfds[ev->used].events = events; bitset_set_bit(ev->sigbset, fd); return ev->used++; } }
static int fdevent_poll_event_set(fdevents *ev, int fde_ndx, int fd, int events) { int pevents = 0; if (events & FDEVENT_IN) pevents |= POLLIN; if (events & FDEVENT_OUT) pevents |= POLLOUT; if (events & FDEVENT_RDHUP) pevents |= POLLRDHUP; /* known index */ if (fde_ndx != -1) { if (ev->pollfds[fde_ndx].fd == fd) { ev->pollfds[fde_ndx].events = pevents; return fde_ndx; } log_error_write(ev->srv, __FILE__, __LINE__, "SdD", "set: ", fde_ndx, ev->pollfds[fde_ndx].fd); SEGFAULT(); } if (ev->unused.used > 0) { int k = ev->unused.ptr[--ev->unused.used]; ev->pollfds[k].fd = fd; ev->pollfds[k].events = pevents; return k; } else { if (ev->size == 0) { ev->size = 16; ev->pollfds = malloc(sizeof(*ev->pollfds) * ev->size); force_assert(NULL != ev->pollfds); } else if (ev->size == ev->used) { ev->size += 16; ev->pollfds = realloc(ev->pollfds, sizeof(*ev->pollfds) * ev->size); force_assert(NULL != ev->pollfds); } ev->pollfds[ev->used].fd = fd; ev->pollfds[ev->used].events = pevents; return ev->used++; } }
static int fdevent_linux_sysepoll_event_del(fdevents * ev, int fde_ndx, int fd) { /** * struct epoll_event * { * __uinit32_t events; //epoll事件标记位,每一位代表一种事件类型。 * epoll_data_t data; //用户数据。 * } * * typedef union epoll_data * { * void *ptr; * int fd; * __uint32_t u32; * __uint64_t u64; * }epoll_data_t * */ struct epoll_event ep; if (fde_ndx < 0) return -1; memset(&ep, 0, sizeof(ep)); ep.data.fd = fd; ep.data.ptr = NULL; //ep.data是个union,这样不就把上一句的赋值给覆盖了??? //EPOLL_CTL_DEL : 将fd从ev->epoll_fd中删除,不在对fd进行监听,最后一个参数 //可以是NULL。将被忽略。 //注:kernel 2.6.9以前,最后一个参数不可以为NULL,尽管它在函数中被忽略。之后的内核 //修正了这一问题。 if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, fd, &ep)) { fprintf(stderr, "%s.%d: epoll_ctl failed: %s, dying\n", __FILE__, __LINE__, strerror(errno)); SEGFAULT(); return 0; } return -1; }
static int fdevent_linux_sysepoll_event_del(fdevents *ev, iosocket *sock) { struct epoll_event ep; if (sock->fde_ndx < 0) return -1; memset(&ep, 0, sizeof(ep)); ep.data.fd = sock->fd; ep.data.ptr = NULL; if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, sock->fd, &ep)) { SEGFAULT("epoll_ctl (del) failed on fd=%d: %s", sock->fd, strerror(errno)); return 0; } sock->fde_ndx = -1; return 0; }
static int fdevent_linux_sysepoll_event_del(fdevents *ev, int fde_ndx, int fd) { struct epoll_event ep; if (fde_ndx < 0) return -1; memset(&ep, 0, sizeof(ep)); ep.data.fd = fd; ep.data.ptr = NULL; if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, fd, &ep)) { fprintf(stderr, "%s.%d: epoll_ctl failed: %s, dying\n", __FILE__, __LINE__, strerror(errno)); SEGFAULT(); return 0; } return -1; }
static int fdevent_linux_sysepoll_event_del(fdevents *ev, int fde_ndx, int fd) { struct epoll_event ep; if (fde_ndx < 0) return -1; memset(&ep, 0, sizeof(ep)); ep.data.fd = fd; ep.data.ptr = NULL; if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, fd, &ep)) { log_error_write(ev->srv, __FILE__, __LINE__, "SSS", "epoll_ctl failed: ", strerror(errno), ", dying"); SEGFAULT(); return 0; } return -1; }
static int fdevent_linux_sysepoll_event_set(fdevents *ev, int fde_ndx, int fd, int events) { struct epoll_event ep; int add = 0; if (fde_ndx == -1) add = 1; memset(&ep, 0, sizeof(ep)); ep.events = 0; if (events & FDEVENT_IN) ep.events |= EPOLLIN; if (events & FDEVENT_OUT) ep.events |= EPOLLOUT; /** * * with EPOLLET we don't get a FDEVENT_HUP * if the close is delay after everything has * sent. * */ ep.events |= EPOLLERR | EPOLLHUP /* | EPOLLET */; ep.data.ptr = NULL; ep.data.fd = fd; if (0 != epoll_ctl(ev->epoll_fd, add ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &ep)) { log_error_write(ev->srv, __FILE__, __LINE__, "SSS", "epoll_ctl failed: ", strerror(errno), ", dying"); SEGFAULT(); return 0; } return fd; }
static int fdevent_linux_sysepoll_event_add(fdevents *ev, iosocket *sock, int events) { struct epoll_event ep; int add = 0; /* a new fd */ if (sock->fde_ndx == -1) add = 1; memset(&ep, 0, sizeof(ep)); ep.events = 0; if (events & FDEVENT_IN) ep.events |= EPOLLIN; if (events & FDEVENT_OUT) ep.events |= EPOLLOUT; /** * * with EPOLLET we don't get a FDEVENT_HUP * if the close is delay after everything has * sent. * */ ep.events |= EPOLLERR | EPOLLHUP /* | EPOLLET */; ep.data.ptr = NULL; ep.data.fd = sock->fd; if (0 != epoll_ctl(ev->epoll_fd, add ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, sock->fd, &ep)) { SEGFAULT("epoll_ctl (add/mod) failed on fd=%d: %s", sock->fd, strerror(errno)); return 0; } sock->fde_ndx = sock->fd; return 0; }
static int mod_rrd_create_pipe(server *srv, plugin_data *p) { #ifdef HAVE_FORK pid_t pid; int to_rrdtool_fds[2]; int from_rrdtool_fds[2]; if (pipe(to_rrdtool_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); return -1; } if (pipe(from_rrdtool_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); return -1; } /* fork, execve */ switch (pid = fork()) { case 0: { /* child */ char **args; int argc; int i = 0; char *dash = "-"; /* move stdout to from_rrdtool_fd[1] */ close(STDOUT_FILENO); dup2(from_rrdtool_fds[1], STDOUT_FILENO); close(from_rrdtool_fds[1]); /* not needed */ close(from_rrdtool_fds[0]); /* move the stdin to to_rrdtool_fd[0] */ close(STDIN_FILENO); dup2(to_rrdtool_fds[0], STDIN_FILENO); close(to_rrdtool_fds[0]); /* not needed */ close(to_rrdtool_fds[1]); /* set up args */ argc = 3; args = malloc(sizeof(*args) * argc); i = 0; args[i++] = p->conf.path_rrdtool_bin->ptr; args[i++] = dash; args[i ] = NULL; /* we don't need the client socket */ for (i = 3; i < 256; i++) { close(i); } /* exec the cgi */ execv(args[0], args); /* log_error_write(srv, __FILE__, __LINE__, "sss", "spawing rrdtool failed: ", strerror(errno), args[0]); */ /* */ SEGFAULT(); break; } case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno)); break; default: { /* father */ close(from_rrdtool_fds[1]); close(to_rrdtool_fds[0]); /* register PID and wait for them asyncronously */ p->write_fd = to_rrdtool_fds[1]; p->read_fd = from_rrdtool_fds[0]; p->rrdtool_pid = pid; fd_close_on_exec(p->write_fd); fd_close_on_exec(p->read_fd); break; } } return 0; #else return -1; #endif }
static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) { pid_t pid; #ifdef HAVE_IPV6 char b2[INET6_ADDRSTRLEN + 1]; #endif int to_cgi_fds[2]; int from_cgi_fds[2]; struct stat st; #ifndef __WIN32 if (cgi_handler->used > 1) { /* stat the exec file */ if (-1 == (stat(cgi_handler->ptr, &st))) { log_error_write(srv, __FILE__, __LINE__, "sbss", "stat for cgi-handler", cgi_handler, "failed:", strerror(errno)); return -1; } } if (pipe(to_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } if (pipe(from_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } /* fork, execve */ switch (pid = fork()) { case 0: { /* child */ char **args; int argc; int i = 0; char buf[32]; size_t n; char_array env; char *c; const char *s; server_socket *srv_sock = con->srv_socket; /* move stdout to from_cgi_fd[1] */ close(STDOUT_FILENO); dup2(from_cgi_fds[1], STDOUT_FILENO); close(from_cgi_fds[1]); /* not needed */ close(from_cgi_fds[0]); /* move the stdin to to_cgi_fd[0] */ close(STDIN_FILENO); dup2(to_cgi_fds[0], STDIN_FILENO); close(to_cgi_fds[0]); /* not needed */ close(to_cgi_fds[1]); /* HACK: * this is not nice, but it works * * we feed the stderr of the CGI to our errorlog, if possible */ if (srv->errorlog_mode == ERRORLOG_FILE) { close(STDERR_FILENO); dup2(srv->errorlog_fd, STDERR_FILENO); } /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION)); if (!buffer_is_empty(con->server_name)) { cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), CONST_BUF_LEN(con->server_name)); } else { #ifdef HAVE_IPV6 s = inet_ntop(srv_sock->addr.plain.sa_family, srv_sock->addr.plain.sa_family == AF_INET6 ? (const void *) &(srv_sock->addr.ipv6.sin6_addr) : (const void *) &(srv_sock->addr.ipv4.sin_addr), b2, sizeof(b2)-1); #else s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); #endif cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); } cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); s = get_http_version_name(con->request.http_version); cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); ltostr(buf, #ifdef HAVE_IPV6 ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) #else ntohs(srv_sock->addr.ipv4.sin_port) #endif ); cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); #ifdef HAVE_IPV6 s = inet_ntop(srv_sock->addr.plain.sa_family, srv_sock->addr.plain.sa_family == AF_INET6 ? (const void *) &(srv_sock->addr.ipv6.sin6_addr) : (const void *) &(srv_sock->addr.ipv4.sin_addr), b2, sizeof(b2)-1); #else s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); #endif cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); s = get_http_method_name(con->request.http_method); cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); if (!buffer_is_empty(con->request.pathinfo)) { cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); } cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); if (!buffer_is_empty(con->uri.query)) { cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); } if (!buffer_is_empty(con->request.orig_uri)) { cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); } #ifdef HAVE_IPV6 s = inet_ntop(con->dst_addr.plain.sa_family, con->dst_addr.plain.sa_family == AF_INET6 ? (const void *) &(con->dst_addr.ipv6.sin6_addr) : (const void *) &(con->dst_addr.ipv4.sin_addr), b2, sizeof(b2)-1); #else s = inet_ntoa(con->dst_addr.ipv4.sin_addr); #endif cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); ltostr(buf, #ifdef HAVE_IPV6 ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port) #else ntohs(con->dst_addr.ipv4.sin_port) #endif ); cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); if (!buffer_is_empty(con->authed_user)) { cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"), CONST_BUF_LEN(con->authed_user)); } /* request.content_length < SSIZE_MAX, see request.c */ ltostr(buf, con->request.content_length); cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); /* for valgrind */ if (NULL != (s = getenv("LD_PRELOAD"))) { cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s)); } if (NULL != (s = getenv("LD_LIBRARY_PATH"))) { cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s)); } #ifdef __CYGWIN__ /* CYGWIN needs SYSTEMROOT */ if (NULL != (s = getenv("SYSTEMROOT"))) { cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s)); } #endif for (n = 0; n < con->request.headers->used; n++) { data_string *ds; ds = (data_string *)con->request.headers->data[n]; if (ds->value->used && ds->key->used) { size_t j; buffer_reset(p->tmp_buf); if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { buffer_copy_string(p->tmp_buf, "HTTP_"); p->tmp_buf->used--; /* strip \0 after HTTP_ */ } buffer_prepare_append(p->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { char cr = '_'; if (light_isalpha(ds->key->ptr[j])) { /* upper-case */ cr = ds->key->ptr[j] & ~32; } else if (light_isdigit(ds->key->ptr[j])) { /* copy */ cr = ds->key->ptr[j]; } p->tmp_buf->ptr[p->tmp_buf->used++] = cr; } p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); } } for (n = 0; n < con->environment->used; n++) { data_string *ds; ds = (data_string *)con->environment->data[n]; if (ds->value->used && ds->key->used) { size_t j; buffer_reset(p->tmp_buf); buffer_prepare_append(p->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { p->tmp_buf->ptr[p->tmp_buf->used++] = isalpha((unsigned char)ds->key->ptr[j]) ? toupper((unsigned char)ds->key->ptr[j]) : '_'; } p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); } } if (env.size == env.used) { env.size += 16; env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr)); } env.ptr[env.used] = NULL; /* set up args */ argc = 3; args = malloc(sizeof(*args) * argc); i = 0; if (cgi_handler->used > 1) { args[i++] = cgi_handler->ptr; } args[i++] = con->physical.path->ptr; args[i++] = NULL; /* search for the last / */ if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { *c = '\0'; /* change to the physical directory */ if (-1 == chdir(con->physical.path->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); } *c = '/'; } /* we don't need the client socket */ for (i = 3; i < 256; i++) { if (i != srv->errorlog_fd) close(i); } /* exec the cgi */ execve(args[0], args, env.ptr); log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]); /* */ SEGFAULT(); break; } case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); break; default: { handler_ctx *hctx; /* father */ close(from_cgi_fds[1]); close(to_cgi_fds[0]); if (con->request.content_length) { chunkqueue *cq = con->request_content_queue; chunk *c; assert(chunkqueue_length(cq) == (off_t)con->request.content_length); /* there is content to send */ for (c = cq->first; c; c = cq->first) { int r = 0; /* copy all chunks */ switch(c->type) { case FILE_CHUNK: if (c->file.mmap.start == MAP_FAILED) { if (-1 == c->file.fd && /* open the file if not already open */ -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); close(from_cgi_fds[0]); close(to_cgi_fds[1]); return -1; } c->file.mmap.length = c->file.length; if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", strerror(errno), c->file.name, c->file.fd); close(from_cgi_fds[0]); close(to_cgi_fds[1]); return -1; } close(c->file.fd); c->file.fd = -1; /* chunk_reset() or chunk_free() will cleanup for us */ } if ((r = write(to_cgi_fds[1], c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) { switch(errno) { case ENOSPC: con->http_status = 507; break; default: con->http_status = 403; break; } } break; case MEM_CHUNK: if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) { switch(errno) { case ENOSPC: con->http_status = 507; break; default: con->http_status = 403; break; } } break; case UNUSED_CHUNK: break; } if (r > 0) { c->offset += r; cq->bytes_out += r; } else { break; } chunkqueue_remove_finished_chunks(cq); } } close(to_cgi_fds[1]); /* register PID and wait for them asyncronously */ con->mode = p->id; buffer_reset(con->physical.path); hctx = cgi_handler_ctx_init(); hctx->remote_conn = con; hctx->plugin_data = p; hctx->pid = pid; hctx->fd = from_cgi_fds[0]; hctx->fde_ndx = -1; con->plugin_ctx[p->id] = hctx; fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); log_error_write(srv, __FILE__, __LINE__, "sd", "cgi close:", hctx->fd); close(hctx->fd); cgi_handler_ctx_free(hctx); con->plugin_ctx[p->id] = NULL; return -1; } break; } } return 0; #else return -1; #endif }
static int http_response_parse_range(server *srv, connection *con, plugin_data *p) { int multipart = 0; int error; off_t start, end; const char *s, *minus; char *boundary = "fkj49sn38dcn3"; data_string *ds; stat_cache_entry *sce = NULL; buffer *content_type = NULL; if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { SEGFAULT(); } start = 0; end = sce->st.st_size - 1; con->response.content_length = 0; if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Type"))) { content_type = ds->value; } for (s = con->request.http_range, error = 0; !error && *s && NULL != (minus = strchr(s, '-')); ) { char *err; off_t la, le; if (s == minus) { /* -<stop> */ le = strtoll(s, &err, 10); if (le == 0) { /* RFC 2616 - 14.35.1 */ con->http_status = 416; error = 1; } else if (*err == '\0') { /* end */ s = err; end = sce->st.st_size - 1; start = sce->st.st_size + le; } else if (*err == ',') { multipart = 1; s = err + 1; end = sce->st.st_size - 1; start = sce->st.st_size + le; } else { error = 1; } } else if (*(minus+1) == '\0' || *(minus+1) == ',') { /* <start>- */ la = strtoll(s, &err, 10); if (err == minus) { /* ok */ if (*(err + 1) == '\0') { s = err + 1; end = sce->st.st_size - 1; start = la; } else if (*(err + 1) == ',') { multipart = 1; s = err + 2; end = sce->st.st_size - 1; start = la; } else { error = 1; } } else { /* error */ error = 1; } } else { /* <start>-<stop> */ la = strtoll(s, &err, 10); if (err == minus) { le = strtoll(minus+1, &err, 10); /* RFC 2616 - 14.35.1 */ if (la > le) { error = 1; } if (*err == '\0') { /* ok, end*/ s = err; end = le; start = la; } else if (*err == ',') { multipart = 1; s = err + 1; end = le; start = la; } else { /* error */ error = 1; } } else { /* error */ error = 1; } } if (!error) { if (start < 0) start = 0; /* RFC 2616 - 14.35.1 */ if (end > sce->st.st_size - 1) end = sce->st.st_size - 1; if (start > sce->st.st_size - 1) { error = 1; con->http_status = 416; } } if (!error) { if (multipart) { /* write boundary-header */ buffer *b; b = chunkqueue_get_append_buffer(con->write_queue); buffer_copy_string_len(b, CONST_STR_LEN("\r\n--")); buffer_append_string(b, boundary); /* write Content-Range */ buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes ")); buffer_append_off_t(b, start); buffer_append_string_len(b, CONST_STR_LEN("-")); buffer_append_off_t(b, end); buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_off_t(b, sce->st.st_size); buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: ")); buffer_append_string_buffer(b, content_type); /* write END-OF-HEADER */ buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); con->response.content_length += b->used - 1; } chunkqueue_append_file(con->write_queue, con->physical.path, start, end - start + 1); con->response.content_length += end - start + 1; } } /* something went wrong */ if (error) return -1; if (multipart) { /* add boundary end */ buffer *b; b = chunkqueue_get_append_buffer(con->write_queue); buffer_copy_string_len(b, "\r\n--", 4); buffer_append_string(b, boundary); buffer_append_string_len(b, "--\r\n", 4); con->response.content_length += b->used - 1; /* set header-fields */ buffer_copy_string_len(p->range_buf, CONST_STR_LEN("multipart/byteranges; boundary=")); buffer_append_string(p->range_buf, boundary); /* overwrite content-type */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf)); } else { /* add Content-Range-header */ buffer_copy_string_len(p->range_buf, CONST_STR_LEN("bytes ")); buffer_append_off_t(p->range_buf, start); buffer_append_string_len(p->range_buf, CONST_STR_LEN("-")); buffer_append_off_t(p->range_buf, end); buffer_append_string_len(p->range_buf, CONST_STR_LEN("/")); buffer_append_off_t(p->range_buf, sce->st.st_size); response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(p->range_buf)); } /* ok, the file is set-up */ return 0; }
int main (int argc, char **argv) { server *srv = NULL; int print_config = 0; int test_config = 0; int i_am_root; int o; int num_childs = 0; int pid_fd = -1, fd; size_t i; #ifdef HAVE_SIGACTION struct sigaction act; #endif #ifdef HAVE_GETRLIMIT struct rlimit rlim; #endif #ifdef USE_ALARM struct itimerval interval; interval.it_interval.tv_sec = 1; interval.it_interval.tv_usec = 0; interval.it_value.tv_sec = 1; interval.it_value.tv_usec = 0; #endif /* for nice %b handling in strfime() */ setlocale(LC_TIME, "C"); if (NULL == (srv = server_init())) { fprintf(stderr, "did this really happen?\n"); return -1; } /* init structs done */ srv->srvconf.port = 0; #ifdef HAVE_GETUID i_am_root = (getuid() == 0); #else i_am_root = 0; #endif srv->srvconf.dont_daemonize = 0; while(-1 != (o = getopt(argc, argv, "f:m:hvVDpt"))) { switch(o) { case 'f': if (srv->config_storage) { log_error_write(srv, __FILE__, __LINE__, "s", "Can only read one config file. Use the include command to use multiple config files."); server_free(srv); return -1; } if (config_read(srv, optarg)) { server_free(srv); return -1; } break; case 'm': buffer_copy_string(srv->srvconf.modules_dir, optarg); break; case 'p': print_config = 1; break; case 't': test_config = 1; break; case 'D': srv->srvconf.dont_daemonize = 1; break; case 'v': show_version(); return 0; case 'V': show_features(); return 0; case 'h': show_help(); return 0; default: show_help(); server_free(srv); return -1; } } if (!srv->config_storage) { log_error_write(srv, __FILE__, __LINE__, "s", "No configuration available. Try using -f option."); server_free(srv); return -1; } if (print_config) { data_unset *dc = srv->config_context->data[0]; if (dc) { dc->print(dc, 0); fprintf(stdout, "\n"); } else { /* shouldn't happend */ fprintf(stderr, "global config not found\n"); } } if (test_config) { printf("Syntax OK\n"); } if (test_config || print_config) { server_free(srv); return 0; } /* close stdin and stdout, as they are not needed */ openDevNull(STDIN_FILENO); openDevNull(STDOUT_FILENO); if (0 != config_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting default values failed"); server_free(srv); return -1; } /* UID handling */ #ifdef HAVE_GETUID if (!i_am_root && issetugid()) { /* we are setuid-root */ log_error_write(srv, __FILE__, __LINE__, "s", "Are you nuts ? Don't apply a SUID bit to this binary"); server_free(srv); return -1; } #endif /* check document-root */ if (srv->config_storage[0]->document_root->used <= 1) { log_error_write(srv, __FILE__, __LINE__, "s", "document-root is not set\n"); server_free(srv); return -1; } if (plugins_load(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "loading plugins finally failed"); plugins_free(srv); server_free(srv); return -1; } /* open pid file BEFORE chroot */ if (srv->srvconf.pid_file->used) { if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { struct stat st; if (errno != EEXIST) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } if (0 != stat(srv->srvconf.pid_file->ptr, &st)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "stating existing pid-file failed:", srv->srvconf.pid_file, strerror(errno)); } if (!S_ISREG(st.st_mode)) { log_error_write(srv, __FILE__, __LINE__, "sb", "pid-file exists and isn't regular file:", srv->srvconf.pid_file); return -1; } if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* select limits itself * * as it is a hard limit and will lead to a segfault we add some safety * */ srv->max_fds = FD_SETSIZE - 200; } else { srv->max_fds = 4096; } if (i_am_root) { struct group *grp = NULL; struct passwd *pwd = NULL; int use_rlimit = 1; #ifdef HAVE_VALGRIND_VALGRIND_H if (RUNNING_ON_VALGRIND) use_rlimit = 0; #endif #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); return -1; } if (use_rlimit && srv->srvconf.max_fds) { /* set rlimits */ rlim.rlim_cur = srv->srvconf.max_fds; rlim.rlim_max = srv->srvconf.max_fds; if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", strerror(errno)); return -1; } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* set core file rlimit, if enable_cores is set */ if (use_rlimit && srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ if (srv->max_fds > ((int)FD_SETSIZE) - 200) { log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } #ifdef HAVE_PWD_H /* set user and group */ if (srv->srvconf.username->used) { if (NULL == (pwd = getpwnam(srv->srvconf.username->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find username", srv->srvconf.username); return -1; } if (pwd->pw_uid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set uid to 0\n"); return -1; } } if (srv->srvconf.groupname->used) { if (NULL == (grp = getgrnam(srv->srvconf.groupname->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find groupname", srv->srvconf.groupname); return -1; } if (grp->gr_gid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set gid to 0\n"); return -1; } } #endif /* we need root-perms for port < 1024 */ if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } #ifdef HAVE_PWD_H /* * Change group before chroot, when we have access * to /etc/group * */ if (NULL != grp) { setgid(grp->gr_gid); setgroups(0, NULL); if (srv->srvconf.username->used) { initgroups(srv->srvconf.username->ptr, grp->gr_gid); } } #endif #ifdef HAVE_CHROOT if (srv->srvconf.changeroot->used) { tzset(); if (-1 == chroot(srv->srvconf.changeroot->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ss", "chroot failed: ", strerror(errno)); return -1; } if (-1 == chdir("/")) { log_error_write(srv, __FILE__, __LINE__, "ss", "chdir failed: ", strerror(errno)); return -1; } } #endif #ifdef HAVE_PWD_H /* drop root privs */ if (NULL != pwd) { setuid(pwd->pw_uid); } #endif #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) /** * on IRIX 6.5.30 they have prctl() but no DUMPABLE */ if (srv->srvconf.enable_cores) { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } #endif } else { #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); return -1; } /** * we are not root can can't increase the fd-limit, but we can reduce it */ if (srv->srvconf.max_fds && srv->srvconf.max_fds < rlim.rlim_cur) { /* set rlimits */ rlim.rlim_cur = srv->srvconf.max_fds; if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", strerror(errno)); return -1; } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* set core file rlimit, if enable_cores is set */ if (srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ if (srv->max_fds > ((int)FD_SETSIZE) - 200) { log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } } /* set max-conns */ if (srv->srvconf.max_conns > srv->max_fds/2) { /* we can't have more connections than max-fds/2 */ log_error_write(srv, __FILE__, __LINE__, "sdd", "can't have more connections than fds/2: ", srv->srvconf.max_conns, srv->max_fds); srv->max_conns = srv->max_fds/2; } else if (srv->srvconf.max_conns) { /* otherwise respect the wishes of the user */ srv->max_conns = srv->srvconf.max_conns; } else { /* or use the default: we really don't want to hit max-fds */ srv->max_conns = srv->max_fds/3; } if (HANDLER_GO_ON != plugins_call_init(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Initialization of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } #ifdef HAVE_FORK /* network is up, let's deamonize ourself */ if (srv->srvconf.dont_daemonize == 0) daemonize(); #endif srv->gid = getgid(); srv->uid = getuid(); /* write pid file */ if (pid_fd != -1) { buffer_copy_long(srv->tmp_buf, getpid()); buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); write(pid_fd, srv->tmp_buf->ptr, srv->tmp_buf->used - 1); close(pid_fd); pid_fd = -1; } /* Close stderr ASAP in the child process to make sure that nothing * is being written to that fd which may not be valid anymore. */ if (-1 == log_error_open(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Opening errorlog failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* dump unused config-keys */ for (i = 0; i < srv->config_context->used; i++) { array *config = ((data_config *)srv->config_context->data[i])->value; size_t j; for (j = 0; config && j < config->used; j++) { data_unset *du = config->data[j]; /* all var.* is known as user defined variable */ if (strncmp(du->key->ptr, "var.", sizeof("var.") - 1) == 0) { continue; } if (NULL == array_get_element(srv->config_touched, du->key->ptr)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "WARNING: unknown config-key:", du->key, "(ignored)"); } } } if (srv->config_unsupported) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains unsupported keys. Going down."); } if (srv->config_deprecated) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains deprecated keys. Going down."); } if (srv->config_unsupported || srv->config_deprecated) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } #ifdef HAVE_SIGACTION memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); sigaction(SIGUSR1, &act, NULL); # if defined(SA_SIGINFO) act.sa_sigaction = sigaction_handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; # else act.sa_handler = signal_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; # endif sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGALRM, &act, NULL); sigaction(SIGCHLD, &act, NULL); #elif defined(HAVE_SIGNAL) /* ignore the SIGPIPE from sendfile() */ signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGALRM, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); signal(SIGCHLD, signal_handler); signal(SIGINT, signal_handler); #endif #ifdef USE_ALARM signal(SIGALRM, signal_handler); /* setup periodic timer (1 second) */ if (setitimer(ITIMER_REAL, &interval, NULL)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed"); return -1; } getitimer(ITIMER_REAL, &interval); #endif #ifdef HAVE_FORK /* start watcher and workers */ num_childs = srv->srvconf.max_worker; if (num_childs > 0) { int child = 0; while (!child && !srv_shutdown && !graceful_shutdown) { if (num_childs > 0) { switch (fork()) { case -1: return -1; case 0: child = 1; break; default: num_childs--; break; } } else { int status; if (-1 != wait(&status)) { /** * one of our workers went away */ num_childs++; } else { switch (errno) { case EINTR: /** * if we receive a SIGHUP we have to close our logs ourself as we don't * have the mainloop who can help us here */ if (handle_sig_hup) { handle_sig_hup = 0; log_error_cycle(srv); /** * forward to all procs in the process-group * * we also send it ourself */ if (!forwarded_sig_hup) { forwarded_sig_hup = 1; kill(0, SIGHUP); } } break; default: break; } } } } /** * for the parent this is the exit-point */ if (!child) { /** * kill all children too */ if (graceful_shutdown) { kill(0, SIGINT); } else if (srv_shutdown) { kill(0, SIGTERM); } log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; } } #endif if (NULL == (srv->ev = fdevent_init(srv, srv->max_fds + 1, srv->event_handler))) { log_error_write(srv, __FILE__, __LINE__, "s", "fdevent_init failed"); return -1; } /* libev backend overwrites our SIGCHLD handler and calls waitpid on SIGCHLD; we want our own SIGCHLD handling. */ #ifdef HAVE_SIGACTION sigaction(SIGCHLD, &act, NULL); #elif defined(HAVE_SIGNAL) signal(SIGCHLD, signal_handler); #endif /* * kqueue() is called here, select resets its internals, * all server sockets get their handlers * * */ if (0 != network_register_fdevents(srv)) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* might fail if user is using fam (not gamin) and famd isn't running */ if (NULL == (srv->stat_cache = stat_cache_init())) { log_error_write(srv, __FILE__, __LINE__, "s", "stat-cache could not be setup, dieing."); return -1; } #ifdef HAVE_FAM_H /* setup FAM */ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (0 != FAMOpen2(srv->stat_cache->fam, "lighttpd")) { log_error_write(srv, __FILE__, __LINE__, "s", "could not open a fam connection, dieing."); return -1; } #ifdef HAVE_FAMNOEXISTS FAMNoExists(srv->stat_cache->fam); #endif srv->stat_cache->fam_fcce_ndx = -1; fdevent_register(srv->ev, FAMCONNECTION_GETFD(srv->stat_cache->fam), stat_cache_handle_fdevent, NULL); fdevent_event_set(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); } #endif /* get the current number of FDs */ srv->cur_fds = open("/dev/null", O_RDONLY); close(srv->cur_fds); for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; if (-1 == fdevent_fcntl_set(srv->ev, srv_socket->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno)); return -1; } } /* main-loop */ while (!srv_shutdown) { int n; size_t ndx; time_t min_ts; if (handle_sig_hup) { handler_t r; /* reset notification */ handle_sig_hup = 0; /* cycle logfiles */ switch(r = plugins_call_handle_sighup(srv)) { case HANDLER_GO_ON: break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "sighup-handler return with an error", r); break; } if (-1 == log_error_cycle(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "cycling errorlog failed, dying"); return -1; } else { #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "logfiles cycled UID =", last_sighup_info.si_uid, "PID =", last_sighup_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "logfiles cycled"); #endif } } if (handle_sig_alarm) { /* a new second */ #ifdef USE_ALARM /* reset notification */ handle_sig_alarm = 0; #endif /* get current time */ min_ts = time(NULL); if (min_ts != srv->cur_ts) { int cs = 0; connections *conns = srv->conns; handler_t r; switch(r = plugins_call_handle_trigger(srv)) { case HANDLER_GO_ON: break; case HANDLER_ERROR: log_error_write(srv, __FILE__, __LINE__, "s", "one of the triggers failed"); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } /* trigger waitpid */ srv->cur_ts = min_ts; /* cleanup stat-cache */ stat_cache_trigger_cleanup(srv); /** * check all connections for timeouts * */ for (ndx = 0; ndx < conns->used; ndx++) { int changed = 0; connection *con; int t_diff; con = conns->ptr[ndx]; if (con->state == CON_STATE_READ || con->state == CON_STATE_READ_POST) { if (con->request_count == 1) { if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { /* time - out */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } else { if (srv->cur_ts - con->read_idle_ts > con->keep_alive_idle) { /* time - out */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } } if ((con->state == CON_STATE_WRITE) && (con->write_request_ts != 0)) { #if 0 if (srv->cur_ts - con->write_request_ts > 60) { log_error_write(srv, __FILE__, __LINE__, "sdd", "connection closed - pre-write-request-timeout:", con->fd, srv->cur_ts - con->write_request_ts); } #endif if (srv->cur_ts - con->write_request_ts > con->conf.max_write_idle) { /* time - out */ if (con->conf.log_timeouts) { log_error_write(srv, __FILE__, __LINE__, "sbsosds", "NOTE: a request for", con->request.uri, "timed out after writing", con->bytes_written, "bytes. We waited", (int)con->conf.max_write_idle, "seconds. If this a problem increase server.max-write-idle"); } connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } if (con->state == CON_STATE_CLOSE && (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT)) { changed = 1; } /* we don't like div by zero */ if (0 == (t_diff = srv->cur_ts - con->connection_start)) t_diff = 1; if (con->traffic_limit_reached && (con->conf.kbytes_per_second == 0 || ((con->bytes_written / t_diff) < con->conf.kbytes_per_second * 1024))) { /* enable connection again */ con->traffic_limit_reached = 0; changed = 1; } if (changed) { connection_state_machine(srv, con); } con->bytes_written_cur_second = 0; *(con->conf.global_bytes_per_second_cnt_ptr) = 0; #if 0 if (cs == 0) { fprintf(stderr, "connection-state: "); cs = 1; } fprintf(stderr, "c[%d,%d]: %s ", con->fd, con->fcgi.fd, connection_get_state(con->state)); #endif } if (cs == 1) fprintf(stderr, "\n"); } } if (srv->sockets_disabled) { /* our server sockets are disabled, why ? */ if ((srv->cur_fds + srv->want_fds < srv->max_fds * 8 / 10) && /* we have enough unused fds */ (srv->conns->used <= srv->max_conns * 9 / 10) && (0 == graceful_shutdown)) { for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_set(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); } log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets enabled again"); srv->sockets_disabled = 0; } } else { if ((srv->cur_fds + srv->want_fds > srv->max_fds * 9 / 10) || /* out of fds */ (srv->conns->used >= srv->max_conns) || /* out of connections */ (graceful_shutdown)) { /* graceful_shutdown */ /* disable server-fds */ for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd); if (graceful_shutdown) { /* we don't want this socket anymore, * * closing it right away will make it possible for * the next lighttpd to take over (graceful restart) * */ fdevent_unregister(srv->ev, srv_socket->fd); close(srv_socket->fd); srv_socket->fd = -1; /* network_close() will cleanup after us */ if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } } } if (graceful_shutdown) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] graceful shutdown started"); } else if (srv->conns->used >= srv->max_conns) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, connection limit reached"); } else { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, out-of-fds"); } srv->sockets_disabled = 1; } } if (graceful_shutdown && srv->conns->used == 0) { /* we are in graceful shutdown phase and all connections are closed * we are ready to terminate without harming anyone */ srv_shutdown = 1; } /* we still have some fds to share */ if (srv->want_fds) { /* check the fdwaitqueue for waiting fds */ int free_fds = srv->max_fds - srv->cur_fds - 16; connection *con; for (; free_fds > 0 && NULL != (con = fdwaitqueue_unshift(srv, srv->fdwaitqueue)); free_fds--) { connection_state_machine(srv, con); srv->want_fds--; } } if ((n = fdevent_poll(srv->ev, 1000)) > 0) { /* n is the number of events */ int revents; int fd_ndx; #if 0 if (n > 0) { log_error_write(srv, __FILE__, __LINE__, "sd", "polls:", n); } #endif fd_ndx = -1; do { fdevent_handler handler; void *context; handler_t r; fd_ndx = fdevent_event_next_fdndx (srv->ev, fd_ndx); if (-1 == fd_ndx) break; /* not all fdevent handlers know how many fds got an event */ revents = fdevent_event_get_revent (srv->ev, fd_ndx); fd = fdevent_event_get_fd (srv->ev, fd_ndx); handler = fdevent_get_handler(srv->ev, fd); context = fdevent_get_context(srv->ev, fd); /* connection_handle_fdevent needs a joblist_append */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sdd", "event for", fd, revents); #endif switch (r = (*handler)(srv, context, revents)) { case HANDLER_FINISHED: case HANDLER_GO_ON: case HANDLER_WAIT_FOR_EVENT: case HANDLER_WAIT_FOR_FD: break; case HANDLER_ERROR: /* should never happen */ SEGFAULT(); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } } while (--n > 0); } else if (n < 0 && errno != EINTR) { log_error_write(srv, __FILE__, __LINE__, "ss", "fdevent_poll failed:", strerror(errno)); } for (ndx = 0; ndx < srv->joblist->used; ndx++) { connection *con = srv->joblist->ptr[ndx]; handler_t r; connection_state_machine(srv, con); switch(r = plugins_call_handle_joblist(srv, con)) { case HANDLER_FINISHED: case HANDLER_GO_ON: break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } con->in_joblist = 0; } srv->joblist->used = 0; } if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0 && 0 == graceful_shutdown) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "server stopped by UID =", last_sigterm_info.si_uid, "PID =", last_sigterm_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "server stopped"); #endif /* clean-up */ log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; }
int fdevent_poll(fdevents *ev, int timeout_ms) { if (ev->poll == NULL) SEGFAULT(); return ev->poll(ev, timeout_ms); }
static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const char **l, size_t n, stat_cache_entry *sce) { size_t i, ssicmd = 0; char buf[255]; buffer *b = NULL; struct { const char *var; enum { SSI_UNSET, SSI_ECHO, SSI_FSIZE, SSI_INCLUDE, SSI_FLASTMOD, SSI_CONFIG, SSI_PRINTENV, SSI_SET, SSI_IF, SSI_ELIF, SSI_ELSE, SSI_ENDIF, SSI_EXEC } type; } ssicmds[] = { { "echo", SSI_ECHO }, { "include", SSI_INCLUDE }, { "flastmod", SSI_FLASTMOD }, { "fsize", SSI_FSIZE }, { "config", SSI_CONFIG }, { "printenv", SSI_PRINTENV }, { "set", SSI_SET }, { "if", SSI_IF }, { "elif", SSI_ELIF }, { "endif", SSI_ENDIF }, { "else", SSI_ELSE }, { "exec", SSI_EXEC }, { NULL, SSI_UNSET } }; for (i = 0; ssicmds[i].var; i++) { if (0 == strcmp(l[1], ssicmds[i].var)) { ssicmd = ssicmds[i].type; break; } } switch(ssicmd) { case SSI_ECHO: { /* echo */ int var = 0; /* int enc = 0; */ const char *var_val = NULL; struct { const char *var; enum { SSI_ECHO_UNSET, SSI_ECHO_DATE_GMT, SSI_ECHO_DATE_LOCAL, SSI_ECHO_DOCUMENT_NAME, SSI_ECHO_DOCUMENT_URI, SSI_ECHO_LAST_MODIFIED, SSI_ECHO_USER_NAME } type; } echovars[] = { { "DATE_GMT", SSI_ECHO_DATE_GMT }, { "DATE_LOCAL", SSI_ECHO_DATE_LOCAL }, { "DOCUMENT_NAME", SSI_ECHO_DOCUMENT_NAME }, { "DOCUMENT_URI", SSI_ECHO_DOCUMENT_URI }, { "LAST_MODIFIED", SSI_ECHO_LAST_MODIFIED }, { "USER_NAME", SSI_ECHO_USER_NAME }, { NULL, SSI_ECHO_UNSET } }; /* struct { const char *var; enum { SSI_ENC_UNSET, SSI_ENC_URL, SSI_ENC_NONE, SSI_ENC_ENTITY } type; } encvars[] = { { "url", SSI_ENC_URL }, { "none", SSI_ENC_NONE }, { "entity", SSI_ENC_ENTITY }, { NULL, SSI_ENC_UNSET } }; */ for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "var")) { int j; var_val = l[i+1]; for (j = 0; echovars[j].var; j++) { if (0 == strcmp(l[i+1], echovars[j].var)) { var = echovars[j].type; break; } } } else if (0 == strcmp(l[i], "encoding")) { /* int j; for (j = 0; encvars[j].var; j++) { if (0 == strcmp(l[i+1], encvars[j].var)) { enc = encvars[j].type; break; } } */ } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (p->if_is_false) break; if (!var_val) { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: ", l[1], "var is missing"); break; } switch(var) { case SSI_ECHO_USER_NAME: { struct passwd *pw; b = chunkqueue_get_append_buffer(con->write_queue); #ifdef HAVE_PWD_H if (NULL == (pw = getpwuid(sce->st.st_uid))) { buffer_copy_long(b, sce->st.st_uid); } else { buffer_copy_string(b, pw->pw_name); } #else buffer_copy_long(b, sce->st.st_uid); #endif break; } case SSI_ECHO_LAST_MODIFIED: { time_t t = sce->st.st_mtime; b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } break; } case SSI_ECHO_DATE_LOCAL: { time_t t = time(NULL); b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } break; } case SSI_ECHO_DATE_GMT: { time_t t = time(NULL); b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) { buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } break; } case SSI_ECHO_DOCUMENT_NAME: { char *sl; b = chunkqueue_get_append_buffer(con->write_queue); if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { buffer_copy_string_buffer(b, con->physical.path); } else { buffer_copy_string(b, sl + 1); } break; } case SSI_ECHO_DOCUMENT_URI: { b = chunkqueue_get_append_buffer(con->write_queue); buffer_copy_string_buffer(b, con->uri.path); break; } default: { data_string *ds; /* check if it is a cgi-var */ b = chunkqueue_get_append_buffer(con->write_queue); if (NULL != (ds = (data_string *)array_get_element(p->ssi_cgi_env, var_val))) { buffer_copy_string_buffer(b, ds->value); } else { buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } break; } } break; } case SSI_INCLUDE: case SSI_FLASTMOD: case SSI_FSIZE: { const char * file_path = NULL, *virt_path = NULL; struct stat st; char *sl; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "file")) { file_path = l[i+1]; } else if (0 == strcmp(l[i], "virtual")) { virt_path = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (!file_path && !virt_path) { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: ", l[1], "file or virtual are missing"); break; } if (file_path && virt_path) { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: ", l[1], "only one of file and virtual is allowed here"); break; } if (p->if_is_false) break; if (file_path) { /* current doc-root */ if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { buffer_copy_string_len(p->stat_fn, CONST_STR_LEN("/")); } else { buffer_copy_string_len(p->stat_fn, con->physical.path->ptr, sl - con->physical.path->ptr + 1); } buffer_copy_string(srv->tmp_buf, file_path); buffer_urldecode_path(srv->tmp_buf); buffer_path_simplify(srv->tmp_buf, srv->tmp_buf); buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); } else { /* virtual */ if (virt_path[0] == '/') { buffer_copy_string(p->stat_fn, virt_path); } else { /* there is always a / */ sl = strrchr(con->uri.path->ptr, '/'); buffer_copy_string_len(p->stat_fn, con->uri.path->ptr, sl - con->uri.path->ptr + 1); buffer_append_string(p->stat_fn, virt_path); } buffer_urldecode_path(p->stat_fn); buffer_path_simplify(srv->tmp_buf, p->stat_fn); /* we have an uri */ buffer_copy_string_buffer(p->stat_fn, con->physical.doc_root); buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); } if (0 == stat(p->stat_fn->ptr, &st)) { time_t t = st.st_mtime; switch (ssicmd) { case SSI_FSIZE: b = chunkqueue_get_append_buffer(con->write_queue); if (p->sizefmt) { int j = 0; const char *abr[] = { " B", " kB", " MB", " GB", " TB", NULL }; off_t s = st.st_size; for (j = 0; s > 1024 && abr[j+1]; s /= 1024, j++); buffer_copy_off_t(b, s); buffer_append_string(b, abr[j]); } else { buffer_copy_off_t(b, st.st_size); } break; case SSI_FLASTMOD: b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } break; case SSI_INCLUDE: chunkqueue_append_file(con->write_queue, p->stat_fn, 0, st.st_size); /* Keep the newest mtime of included files */ if (st.st_mtime > include_file_last_mtime) include_file_last_mtime = st.st_mtime; break; } } else { log_error_write(srv, __FILE__, __LINE__, "sbs", "ssi: stating failed ", p->stat_fn, strerror(errno)); } break; } case SSI_SET: { const char *key = NULL, *val = NULL; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "var")) { key = l[i+1]; } else if (0 == strcmp(l[i], "value")) { val = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (p->if_is_false) break; if (key && val) { data_string *ds; if (NULL == (ds = (data_string *)array_get_unused_element(p->ssi_vars, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string(ds->key, key); buffer_copy_string(ds->value, val); array_insert_unique(p->ssi_vars, (data_unset *)ds); } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: var and value have to be set in", l[0], l[1]); } break; } case SSI_CONFIG: if (p->if_is_false) break; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "timefmt")) { buffer_copy_string(p->timefmt, l[i+1]); } else if (0 == strcmp(l[i], "sizefmt")) { if (0 == strcmp(l[i+1], "abbrev")) { p->sizefmt = 1; } else if (0 == strcmp(l[i+1], "abbrev")) { p->sizefmt = 0; } else { log_error_write(srv, __FILE__, __LINE__, "sssss", "ssi: unknow value for attribute '", l[i], "' for ", l[1], l[i+1]); } } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } break; case SSI_PRINTENV: if (p->if_is_false) break; b = chunkqueue_get_append_buffer(con->write_queue); for (i = 0; i < p->ssi_vars->used; i++) { data_string *ds = (data_string *)p->ssi_vars->data[p->ssi_vars->sorted[i]]; buffer_append_string_buffer(b, ds->key); buffer_append_string_len(b, CONST_STR_LEN("=")); buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML); buffer_append_string_len(b, CONST_STR_LEN("\n")); } for (i = 0; i < p->ssi_cgi_env->used; i++) { data_string *ds = (data_string *)p->ssi_cgi_env->data[p->ssi_cgi_env->sorted[i]]; buffer_append_string_buffer(b, ds->key); buffer_append_string_len(b, CONST_STR_LEN("=")); buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML); buffer_append_string_len(b, CONST_STR_LEN("\n")); } break; case SSI_EXEC: { const char *cmd = NULL; pid_t pid; int from_exec_fds[2]; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "cmd")) { cmd = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (p->if_is_false) break; /* create a return pipe and send output to the html-page * * as exec is assumed evil it is implemented synchronously */ if (!cmd) break; #ifdef HAVE_FORK if (pipe(from_exec_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); return -1; } /* fork, execve */ switch (pid = fork()) { case 0: { /* move stdout to from_rrdtool_fd[1] */ close(STDOUT_FILENO); dup2(from_exec_fds[1], STDOUT_FILENO); close(from_exec_fds[1]); /* not needed */ close(from_exec_fds[0]); /* close stdin */ close(STDIN_FILENO); execl("/bin/sh", "sh", "-c", cmd, (char *)NULL); log_error_write(srv, __FILE__, __LINE__, "sss", "spawing exec failed:", strerror(errno), cmd); /* */ SEGFAULT(); break; } case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); break; default: { /* father */ int status; ssize_t r; int was_interrupted = 0; close(from_exec_fds[1]); /* wait for the client to end */ /* * OpenBSD and Solaris send a EINTR on SIGCHILD even if we ignore it */ do { if (-1 == waitpid(pid, &status, 0)) { if (errno == EINTR) { was_interrupted++; } else { was_interrupted = 0; log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed:", strerror(errno)); } } else if (WIFEXITED(status)) { int toread; /* read everything from client and paste it into the output */ was_interrupted = 0; while(1) { if (ioctl(from_exec_fds[0], FIONREAD, &toread)) { log_error_write(srv, __FILE__, __LINE__, "s", "unexpected end-of-file (perhaps the ssi-exec process died)"); return -1; } if (toread > 0) { b = chunkqueue_get_append_buffer(con->write_queue); buffer_prepare_copy(b, toread + 1); if ((r = read(from_exec_fds[0], b->ptr, b->size - 1)) < 0) { /* read failed */ break; } else { b->used = r; b->ptr[b->used++] = '\0'; } } else { break; } } } else { was_interrupted = 0; log_error_write(srv, __FILE__, __LINE__, "s", "process exited abnormally"); } } while (was_interrupted > 0 && was_interrupted < 4); /* if waitpid() gets interrupted, retry, but max 4 times */ close(from_exec_fds[0]); break; } } #else return -1; #endif break; } case SSI_IF: { const char *expr = NULL; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "expr")) { expr = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (!expr) { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: ", l[1], "expr missing"); break; } if ((!p->if_is_false) && ((p->if_is_false_level == 0) || (p->if_level < p->if_is_false_level))) { switch (ssi_eval_expr(srv, con, p, expr)) { case -1: case 0: p->if_is_false = 1; p->if_is_false_level = p->if_level; break; case 1: p->if_is_false = 0; break; } } p->if_level++; break; } case SSI_ELSE: p->if_level--; if (p->if_is_false) { if ((p->if_level == p->if_is_false_level) && (p->if_is_false_endif == 0)) { p->if_is_false = 0; } } else { p->if_is_false = 1; p->if_is_false_level = p->if_level; } p->if_level++; break; case SSI_ELIF: { const char *expr = NULL; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "expr")) { expr = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (!expr) { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: ", l[1], "expr missing"); break; } p->if_level--; if (p->if_level == p->if_is_false_level) { if ((p->if_is_false) && (p->if_is_false_endif == 0)) { switch (ssi_eval_expr(srv, con, p, expr)) { case -1: case 0: p->if_is_false = 1; p->if_is_false_level = p->if_level; break; case 1: p->if_is_false = 0; break; } } else { p->if_is_false = 1; p->if_is_false_level = p->if_level; p->if_is_false_endif = 1; } } p->if_level++; break; } case SSI_ENDIF: p->if_level--; if (p->if_level == p->if_is_false_level) { p->if_is_false = 0; p->if_is_false_endif = 0; } break; default: log_error_write(srv, __FILE__, __LINE__, "ss", "ssi: unknow ssi-command:", l[1]); break; } return 0; }
static int http_response_parse_range(server *srv, connection *con, plugin_data *p) { int multipart = 0; char *boundary = "fkj49sn38dcn3"; data_string *ds; stat_cache_entry *sce = NULL; buffer *content_type = NULL; buffer *range = NULL; http_req_range *ranges, *r; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Range")))) { range = ds->value; } else { /* we don't have a Range header */ return -1; } if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { SEGFAULT("stat_cache_get_entry(%s) returned %d", SAFE_BUF_STR(con->physical.path), HANDLER_ERROR); } con->response.content_length = 0; if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Content-Type")))) { content_type = ds->value; } /* start the range-header parser * bytes=<num> */ ranges = p->ranges; http_request_range_reset(ranges); switch (http_request_range_parse(range, ranges)) { case PARSE_ERROR: return -1; /* no range valid Range Header */ case PARSE_SUCCESS: break; default: TRACE("%s", "foobar"); return -1; } if (ranges->next) { multipart = 1; } /* patch the '-1' */ for (r = ranges; r; r = r->next) { if (r->start == -1) { /* -<end> * * the last <end> bytes */ r->start = sce->st.st_size - r->end; r->end = sce->st.st_size - 1; } if (r->end == -1) { /* <start>- * all but the first <start> bytes */ r->end = sce->st.st_size - 1; } if (r->end > sce->st.st_size - 1) { /* RFC 2616 - 14.35.1 * * if last-byte-pos not present or > size-of-file * take the size-of-file * * */ r->end = sce->st.st_size - 1; } if (r->start > sce->st.st_size - 1) { /* RFC 2616 - 14.35.1 * * if first-byte-pos > file-size, 416 */ con->http_status = 416; return -1; } if (r->start > r->end) { /* RFC 2616 - 14.35.1 * * if last-byte-pos is present, it has to be >= first-byte-pos * * invalid ranges have to be handle as no Range specified * */ return -1; } } if (r) { /* we ran into an range violation */ return -1; } if (multipart) { buffer *b; for (r = ranges; r; r = r->next) { /* write boundary-header */ b = chunkqueue_get_append_buffer(con->send); buffer_copy_string_len(b, CONST_STR_LEN("\r\n--")); buffer_append_string(b, boundary); /* write Content-Range */ buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes ")); buffer_append_off_t(b, r->start); buffer_append_string_len(b, CONST_STR_LEN("-")); buffer_append_off_t(b, r->end); buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_off_t(b, sce->st.st_size); buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: ")); buffer_append_string_buffer(b, content_type); /* write END-OF-HEADER */ buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); con->response.content_length += b->used - 1; con->send->bytes_in += b->used - 1; chunkqueue_append_file(con->send, con->physical.path, r->start, r->end - r->start + 1); con->response.content_length += r->end - r->start + 1; con->send->bytes_in += r->end - r->start + 1; } /* add boundary end */ b = chunkqueue_get_append_buffer(con->send); buffer_copy_string_len(b, "\r\n--", 4); buffer_append_string(b, boundary); buffer_append_string_len(b, "--\r\n", 4); con->response.content_length += b->used - 1; con->send->bytes_in += b->used - 1; /* set header-fields */ buffer_copy_string_len(p->range_buf, CONST_STR_LEN("multipart/byteranges; boundary=")); buffer_append_string(p->range_buf, boundary); /* overwrite content-type */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf)); } else { r = ranges; chunkqueue_append_file(con->send, con->physical.path, r->start, r->end - r->start + 1); con->response.content_length += r->end - r->start + 1; con->send->bytes_in += r->end - r->start + 1; buffer_copy_string_len(p->range_buf, CONST_STR_LEN("bytes ")); buffer_append_off_t(p->range_buf, r->start); buffer_append_string_len(p->range_buf, CONST_STR_LEN("-")); buffer_append_off_t(p->range_buf, r->end); buffer_append_string_len(p->range_buf, CONST_STR_LEN("/")); buffer_append_off_t(p->range_buf, sce->st.st_size); response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(p->range_buf)); } /* ok, the file is set-up */ return 0; }
static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) { pid_t pid; int to_cgi_fds[2]; int from_cgi_fds[2]; int from_cgi_err_fds[2]; struct stat st; #ifndef _WIN32 if (cgi_handler && cgi_handler->used > 1) { /* stat the exec file */ if (-1 == (stat(cgi_handler->ptr, &st))) { log_error_write(srv, __FILE__, __LINE__, "sbss", "stat for cgi-handler", cgi_handler, "failed:", strerror(errno)); return -1; } } if (pipe(to_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } if (pipe(from_cgi_fds)) { close(to_cgi_fds[0]); close(to_cgi_fds[1]); log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } if (pipe(from_cgi_err_fds)) { close(to_cgi_fds[0]); close(to_cgi_fds[1]); close(from_cgi_fds[0]); close(from_cgi_fds[1]); log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } /* fork, execve */ switch (pid = fork()) { case 0: { /* child */ char **args; int argc; int i = 0; char buf[32]; size_t n; char_array env; char *c; const char *s; server_socket *srv_sock = con->srv_socket; /* move stdout to from_cgi_fd[1] */ close(STDOUT_FILENO); dup2(from_cgi_fds[1], STDOUT_FILENO); close(from_cgi_fds[1]); /* not needed */ close(from_cgi_fds[0]); /* move stderr to from_cgi_err_fd[1] */ close(STDERR_FILENO); dup2(from_cgi_err_fds[1], STDERR_FILENO); close(from_cgi_err_fds[1]); /* not needed */ close(from_cgi_err_fds[0]); /* move the stdin to to_cgi_fd[0] */ close(STDIN_FILENO); dup2(to_cgi_fds[0], STDIN_FILENO); close(to_cgi_fds[0]); /* not needed */ close(to_cgi_fds[1]); /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION)); s = sock_addr_to_p(srv, &srv_sock->addr); cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); /* !!! careful: s maybe reused for SERVER_NAME !!! */ if (!buffer_is_empty(con->server_name)) { size_t len = con->server_name->used - 1; char *colon = strchr(con->server_name->ptr, ':'); if (colon) len = colon - con->server_name->ptr; cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len); } else { /* use SERVER_ADDR */ cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); } cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); s = get_http_version_name(con->request.http_version); cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); LI_ltostr(buf, sock_addr_get_port(&srv_sock->addr)); cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); s = get_http_method_name(con->request.http_method); cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); if (!buffer_is_empty(con->request.pathinfo)) { cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); } cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); if (!buffer_is_empty(con->uri.query)) { cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); } else { /* set a empty QUERY_STRING */ cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")); } if (!buffer_is_empty(con->request.orig_uri)) { cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); } s = sock_addr_to_p(srv, &con->dst_addr); cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); LI_ltostr(buf, sock_addr_get_port(&con->dst_addr)); cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); if (!buffer_is_empty(con->authed_user)) { cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"), CONST_BUF_LEN(con->authed_user)); } #ifdef USE_OPENSSL if (srv_sock->is_ssl) { cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); } #endif /* request.content_length < SSIZE_MAX, see request.c */ if (con->request.content_length > 0) { LI_ltostr(buf, con->request.content_length); cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); } cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); /* for valgrind */ if (NULL != (s = getenv("LD_PRELOAD"))) { cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s)); } if (NULL != (s = getenv("LD_LIBRARY_PATH"))) { cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s)); } #ifdef __CYGWIN__ /* CYGWIN needs SYSTEMROOT */ if (NULL != (s = getenv("SYSTEMROOT"))) { cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s)); } #endif for (n = 0; n < con->request.headers->used; n++) { data_string *ds; ds = (data_string *)con->request.headers->data[n]; if (ds->value->used && ds->key->used) { size_t j; buffer_reset(p->tmp_buf); if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_")); p->tmp_buf->used--; /* strip \0 after HTTP_ */ } buffer_prepare_append(p->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { char cr = '_'; if (light_isalpha(ds->key->ptr[j])) { /* upper-case */ cr = ds->key->ptr[j] & ~32; } else if (light_isdigit(ds->key->ptr[j])) { /* copy */ cr = ds->key->ptr[j]; } p->tmp_buf->ptr[p->tmp_buf->used++] = cr; } p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); } } for (n = 0; n < con->environment->used; n++) { data_string *ds; ds = (data_string *)con->environment->data[n]; if (ds->value->used && ds->key->used) { size_t j; buffer_reset(p->tmp_buf); buffer_prepare_append(p->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { char cr = '_'; if (light_isalpha(ds->key->ptr[j])) { /* upper-case */ cr = ds->key->ptr[j] & ~32; } else if (light_isdigit(ds->key->ptr[j])) { /* copy */ cr = ds->key->ptr[j]; } p->tmp_buf->ptr[p->tmp_buf->used++] = cr; } p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); } } if (env.size == env.used) { env.size += 16; env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr)); } env.ptr[env.used] = NULL; /* set up args */ argc = 3; args = malloc(sizeof(*args) * argc); i = 0; if (cgi_handler && cgi_handler->used > 1) { args[i++] = cgi_handler->ptr; } args[i++] = con->physical.path->ptr; args[i++] = NULL; /* search for the last / */ if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { *c = '\0'; /* change to the physical directory */ if (-1 == chdir(con->physical.path->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); } *c = '/'; } /* we don't need the client socket */ for (i = 3; i < 256; i++) { close(i); } /* exec the cgi */ execve(args[0], args, env.ptr); /* */ SEGFAULT("execve(%s) failed: %s", args[0], strerror(errno)); break; } case -1: /* error */ ERROR("fork() failed: %s", strerror(errno)); close(to_cgi_fds[0]); close(to_cgi_fds[1]); close(from_cgi_fds[0]); close(from_cgi_fds[1]); close(from_cgi_err_fds[0]); close(from_cgi_err_fds[1]); return -1; break; default: { cgi_session *sess; /* father */ close(from_cgi_fds[1]); close(from_cgi_err_fds[1]); close(to_cgi_fds[0]); /* register PID and wait for them asyncronously */ con->mode = p->id; buffer_reset(con->physical.path); sess = cgi_session_init(); sess->remote_con = con; sess->pid = pid; assert(sess->sock); sess->sock->fd = from_cgi_fds[0]; sess->sock->type = IOSOCKET_TYPE_PIPE; sess->sock_err->fd = from_cgi_err_fds[0]; sess->sock_err->type = IOSOCKET_TYPE_PIPE; sess->wb_sock->fd = to_cgi_fds[1]; sess->wb_sock->type = IOSOCKET_TYPE_PIPE; if (-1 == fdevent_fcntl_set(srv->ev, sess->sock)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); cgi_session_free(sess); return -1; } if (-1 == fdevent_fcntl_set(srv->ev, sess->sock_err)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); cgi_session_free(sess); return -1; } con->plugin_ctx[p->id] = sess; fdevent_register(srv->ev, sess->sock, cgi_handle_fdevent, sess); fdevent_event_add(srv->ev, sess->sock, FDEVENT_IN); fdevent_register(srv->ev, sess->sock_err, cgi_handle_err_fdevent, sess); fdevent_event_add(srv->ev, sess->sock_err, FDEVENT_IN); sess->state = CGI_STATE_READ_RESPONSE_HEADER; break; } } return 0; #else return -1; #endif }