static void http_chunk_append_len(server *srv, connection *con, uintmax_t len) { buffer *b; force_assert(NULL != srv); b = srv->tmp_chunk_len; buffer_string_set_length(b, 0); buffer_append_uint_hex(b, len); buffer_append_string_len(b, CONST_STR_LEN("\r\n")); chunkqueue_append_buffer(con->write_queue, b); }
http_auth_require_t * http_auth_require_init (void) { http_auth_require_t *require = calloc(1, sizeof(http_auth_require_t)); force_assert(NULL != require); require->realm = buffer_init(); require->valid_user = 0; require->user = array_init(); require->group = array_init(); require->host = array_init(); return require; }
static void chunkqueue_push_unused_chunk(chunkqueue *cq, chunk *c) { force_assert(NULL != cq && NULL != c); /* keep at max 4 chunks in the 'unused'-cache */ if (cq->unused_chunks > 4) { chunk_free(c); } else { chunk_reset(c); c->next = cq->unused; cq->unused = c; cq->unused_chunks++; } }
static void cgi_pid_add(plugin_data *p, pid_t pid, void *ctx) { buffer_pid_t *r = &(p->cgi_pid); if (r->used == r->size) { r->size += 16; r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size); force_assert(r->ptr); } r->ptr[r->used].pid = pid; r->ptr[r->used].ctx = ctx; ++r->used; }
int fdevent_unregister(fdevents *ev, int fd) { fdnode *fdn; if (!ev) return 0; fdn = ev->fdarray[fd]; force_assert(fdn->events == 0); fdnode_free(fdn); ev->fdarray[fd] = NULL; return 0; }
static void log_write(server *srv, buffer *b) { switch(srv->errorlog_mode) { case ERRORLOG_PIPE: case ERRORLOG_FILE: case ERRORLOG_FD: buffer_append_string_len(b, CONST_STR_LEN("\n")); force_assert(b->used > 0); write(srv->errorlog_fd, b->ptr, b->used - 1); break; case ERRORLOG_SYSLOG: syslog(LOG_ERR, "%s", b->ptr); break; } }
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); force_assert(NULL != ev->unused.ptr); } 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); force_assert(NULL != ev->unused.ptr); } 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; }
chunkqueue *chunkqueue_init(void) { chunkqueue *cq; cq = calloc(1, sizeof(*cq)); force_assert(NULL != cq); cq->first = NULL; cq->last = NULL; cq->unused = NULL; cq->tempdirs = chunkqueue_default_tempdirs; cq->upload_temp_file_size = chunkqueue_default_tempfile_size; return cq; }
int stat_cache_trigger_cleanup(server *srv) { stat_cache *sc; size_t max_ndx = 0, i; int *keys; sc = srv->stat_cache; if (!sc->files) return 0; keys = calloc(1, sizeof(int) * sc->files->size); stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx); for (i = 0; i < max_ndx; i++) { int ndx = keys[i]; splay_tree *node; sc->files = splaytree_splay(sc->files, ndx); node = sc->files; if (node && (node->key == ndx)) { #ifdef DEBUG_STAT_CACHE size_t j; int osize = splaytree_size(sc->files); stat_cache_entry *sce = node->data; #endif stat_cache_entry_free(node->data); sc->files = splaytree_delete(sc->files, ndx); #ifdef DEBUG_STAT_CACHE for (j = 0; j < ctrl.used; j++) { if (ctrl.ptr[j] == ndx) { ctrl.ptr[j] = ctrl.ptr[--ctrl.used]; break; } } force_assert(osize - 1 == splaytree_size(sc->files)); #endif } } free(keys); return 0; }
static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) { int curelem = lua_gettop(L); int result; lua_getglobal(L, varname); if (lua_isstring(L, curelem)) { buffer_copy_string(b, lua_tostring(L, curelem)); result = 0; } else { result = -1; } lua_pop(L, 1); force_assert(curelem == lua_gettop(L)); return result; }
void chunkqueue_steal(chunkqueue *dest, chunkqueue *src, off_t len) { while (len > 0) { chunk *c = src->first; off_t clen = 0, use; if (NULL == c) break; clen = chunk_remaining_length(c); if (0 == clen) { /* drop empty chunk */ src->first = c->next; if (c == src->last) src->last = NULL; chunkqueue_push_unused_chunk(src, c); continue; } use = len >= clen ? clen : len; len -= use; if (use == clen) { /* move complete chunk */ src->first = c->next; if (c == src->last) src->last = NULL; chunkqueue_append_chunk(dest, c); } else { /* partial chunk with length "use" */ switch (c->type) { case MEM_CHUNK: chunkqueue_append_mem(dest, c->mem->ptr + c->offset, use); break; case FILE_CHUNK: /* tempfile flag is in "last" chunk after the split */ chunkqueue_append_file(dest, c->file.name, c->file.start + c->offset, use); break; } c->offset += use; force_assert(0 == len); } src->bytes_out += use; } }
data_array *data_array_init(void) { data_array *ds; ds = calloc(1, sizeof(*ds)); force_assert(NULL != ds); ds->key = buffer_init(); ds->value = array_init(); ds->copy = data_array_copy; ds->free = data_array_free; ds->reset = data_array_reset; ds->insert_dup = data_array_insert_dup; ds->print = data_array_print; ds->type = TYPE_ARRAY; return ds; }
data_string *data_string_init(void) { data_string *ds; ds = calloc(1, sizeof(*ds)); force_assert(ds); ds->key = buffer_init(); ds->value = buffer_init(); ds->copy = data_string_copy; ds->free = data_string_free; ds->reset = data_string_reset; ds->insert_dup = data_string_insert_dup; ds->print = data_string_print; ds->type = TYPE_STRING; return ds; }
static chunk *chunkqueue_get_unused_chunk(chunkqueue *cq) { chunk *c; force_assert(NULL != cq); /* check if we have a unused chunk */ if (0 == cq->unused) { c = chunk_init(); } else { /* take the first element from the list (a stack) */ c = cq->unused; cq->unused = c->next; c->next = NULL; cq->unused_chunks--; } return c; }
static chunk *chunk_init(void) { chunk *c; c = calloc(1, sizeof(*c)); force_assert(NULL != c); c->type = MEM_CHUNK; c->mem = buffer_init(); c->file.name = buffer_init(); c->file.start = c->file.length = c->file.mmap.offset = 0; c->file.fd = -1; c->file.mmap.start = MAP_FAILED; c->file.mmap.length = 0; c->file.is_temp = 0; c->offset = 0; c->next = NULL; return c; }
static int build_doc_root(server *srv, connection *con, plugin_data *p, buffer *out, buffer *host) { stat_cache_entry *sce = NULL; force_assert(!buffer_string_is_empty(p->conf.server_root)); buffer_string_prepare_copy(out, 127); buffer_copy_buffer(out, p->conf.server_root); if (!buffer_string_is_empty(host)) { /* a hostname has to start with a alpha-numerical character * and must not contain a slash "/" */ char *dp; buffer_append_slash(out); if (NULL == (dp = strchr(host->ptr, ':'))) { buffer_append_string_buffer(out, host); } else { buffer_append_string_len(out, host->ptr, dp - host->ptr); } } buffer_append_slash(out); if (buffer_string_length(p->conf.document_root) > 1 && p->conf.document_root->ptr[0] == '/') { buffer_append_string_len(out, p->conf.document_root->ptr + 1, buffer_string_length(p->conf.document_root) - 1); } else { buffer_append_string_buffer(out, p->conf.document_root); buffer_append_slash(out); } if (HANDLER_ERROR == stat_cache_get_entry(srv, con, out, &sce)) { if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), out); } return -1; } else if (!S_ISDIR(sce->st.st_mode)) { return -1; } return 0; }
static int lua_to_c_is_table(lua_State *L, const char *varname) { int curelem; lua_pushstring(L, varname); curelem = lua_gettop(L); lua_gettable(L, LUA_GLOBALSINDEX); /* it should be a table */ if (!lua_istable(L, curelem)) { lua_settop(L, curelem - 1); return 0; } lua_settop(L, curelem - 1); force_assert(curelem - 1 == lua_gettop(L)); return 1; }
data_array *data_array_init(void) { static const struct data_methods fn = { data_array_reset, data_array_copy, data_array_free, data_array_insert_dup, data_array_print, }; data_array *ds; ds = calloc(1, sizeof(*ds)); force_assert(NULL != ds); ds->key = buffer_init(); ds->value = array_init(); ds->type = TYPE_ARRAY; ds->fn = &fn; return ds; }
static int magnet_copy_response_header(server *srv, connection *con, plugin_data *p, lua_State *L) { UNUSED(p); /** * get the environment of the function */ lua_getfenv(L, -1); /* -1 is the function */ /* lighty.header */ lua_getfield(L, -1, "lighty"); /* lighty.* from the env */ force_assert(lua_istable(L, -1)); lua_getfield(L, -1, "header"); /* lighty.header */ if (lua_istable(L, -1)) { /* header is found, and is a table */ lua_pushnil(L); while (lua_next(L, -2) != 0) { if (lua_isstring(L, -1) && lua_isstring(L, -2)) { const char *key, *val; size_t key_len, val_len; key = lua_tolstring(L, -2, &key_len); val = lua_tolstring(L, -1, &val_len); response_header_overwrite(srv, con, key, key_len, val, val_len); } lua_pop(L, 1); } } lua_pop(L, 1); /* pop the header-table */ lua_pop(L, 1); /* pop the lighty-env */ lua_pop(L, 1); /* pop the function env */ return 0; }
static int magnet_copy_response_header(server *srv, connection *con, lua_State *L, int lighty_table_ndx) { force_assert(lua_istable(L, lighty_table_ndx)); lua_getfield(L, lighty_table_ndx, "header"); /* lighty.header */ if (lua_istable(L, -1)) { /* header is found, and is a table */ lua_pushnil(L); while (lua_next(L, -2) != 0) { if (lua_isstring(L, -1) && lua_isstring(L, -2)) { const_buffer key = magnet_checkconstbuffer(L, -2); const_buffer val = magnet_checkconstbuffer(L, -1); response_header_overwrite(srv, con, key.ptr, key.len, val.ptr, val.len); } lua_pop(L, 1); } } lua_pop(L, 1); /* pop lighty.header */ return 0; }
static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) { int curelem; lua_pushstring(L, varname); curelem = lua_gettop(L); lua_gettable(L, LUA_GLOBALSINDEX); /* it should be a table */ if (!lua_isstring(L, curelem)) { lua_settop(L, curelem - 1); return -1; } buffer_copy_string(b, lua_tostring(L, curelem)); lua_pop(L, 1); force_assert(curelem - 1 == lua_gettop(L)); return 0; }
/* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes), * also mmaps and sends complete chunk instead of only small parts - the files * are supposed to be temp files with reasonable chunk sizes. * * Also always use mmap; the files are "trusted", as we created them. */ static ssize_t cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) { chunk* const c = cq->first; off_t offset, toSend, file_end; ssize_t r; size_t mmap_offset, mmap_avail; char *data; force_assert(NULL != c); force_assert(FILE_CHUNK == c->type); force_assert(c->offset >= 0 && c->offset <= c->file.length); offset = c->file.start + c->offset; toSend = c->file.length - c->offset; file_end = c->file.start + c->file.length; /* offset to file end in this chunk */ if (0 == toSend) { chunkqueue_remove_finished_chunks(cq); return 0; } /*(simplified from network_write_no_mmap.c:network_open_file_chunk())*/ UNUSED(con); if (-1 == c->file.fd) { if (-1 == (c->file.fd = fdevent_open_cloexec(c->file.name->ptr, O_RDONLY, 0))) { log_error_write(srv, __FILE__, __LINE__, "ssb", "open failed:", strerror(errno), c->file.name); return -1; } } /* (re)mmap the buffer if range is not covered completely */ if (MAP_FAILED == c->file.mmap.start || offset < c->file.mmap.offset || file_end > (off_t)(c->file.mmap.offset + c->file.mmap.length)) { if (MAP_FAILED != c->file.mmap.start) { munmap(c->file.mmap.start, c->file.mmap.length); c->file.mmap.start = MAP_FAILED; } c->file.mmap.offset = mmap_align_offset(offset); c->file.mmap.length = file_end - c->file.mmap.offset; if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset))) { if (toSend > 65536) toSend = 65536; data = malloc(toSend); force_assert(data); if (-1 == lseek(c->file.fd, offset, SEEK_SET) || 0 >= (toSend = read(c->file.fd, data, toSend))) { if (-1 == toSend) { log_error_write(srv, __FILE__, __LINE__, "ssbdo", "lseek/read failed:", strerror(errno), c->file.name, c->file.fd, offset); } else { /*(0 == toSend)*/ log_error_write(srv, __FILE__, __LINE__, "sbdo", "unexpected EOF (input truncated?):", c->file.name, c->file.fd, offset); } free(data); return -1; } } } if (MAP_FAILED != c->file.mmap.start) { force_assert(offset >= c->file.mmap.offset); mmap_offset = offset - c->file.mmap.offset; force_assert(c->file.mmap.length > mmap_offset); mmap_avail = c->file.mmap.length - mmap_offset; force_assert(toSend <= (off_t) mmap_avail); data = c->file.mmap.start + mmap_offset; } r = write(fd, data, toSend); if (MAP_FAILED == c->file.mmap.start) free(data); if (r < 0) { switch (errno) { case EAGAIN: case EINTR: return 0; case EPIPE: case ECONNRESET: return -2; default: log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), fd); return -1; } } if (r >= 0) { chunkqueue_mark_written(cq, r); } return r; }
static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) { pid_t pid; int to_cgi_fds[2]; int from_cgi_fds[2]; struct stat st; UNUSED(p); #ifndef __WIN32 if (!buffer_string_is_empty(cgi_handler)) { /* 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_cloexec(to_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } if (pipe_cloexec(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; } /* fork, execve */ switch (pid = fork()) { case 0: { /* child */ char **args; int argc; int i = 0; char_array env; char *c; const char *s; http_cgi_opts opts = { 0, 0, NULL, NULL }; /* move stdout to from_cgi_fd[1] */ dup2(from_cgi_fds[1], STDOUT_FILENO); #ifndef FD_CLOEXEC close(from_cgi_fds[1]); /* not needed */ close(from_cgi_fds[0]); #endif /* move the stdin to to_cgi_fd[0] */ dup2(to_cgi_fds[0], STDIN_FILENO); #ifndef FD_CLOEXEC close(to_cgi_fds[0]); /* not needed */ close(to_cgi_fds[1]); #endif /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; http_cgi_headers(srv, con, &opts, cgi_env_add, &env); /* 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 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); force_assert(args); i = 0; if (!buffer_string_is_empty(cgi_handler)) { 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, '/'))) { /* handle special case of file in root directory */ const char* physdir = (c == con->physical.path->ptr) ? "/" : con->physical.path->ptr; /* temporarily shorten con->physical.path to directory without terminating '/' */ *c = '\0'; /* change to the physical directory */ if (-1 == chdir(physdir)) { 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); /* most log files may have been closed/redirected by this point, * though stderr might still point to lighttpd.breakage.log */ perror(args[0]); _exit(1); } case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); close(from_cgi_fds[0]); close(from_cgi_fds[1]); close(to_cgi_fds[0]); close(to_cgi_fds[1]); return -1; default: { /* parent process */ close(from_cgi_fds[1]); close(to_cgi_fds[0]); /* register PID and wait for them asynchronously */ hctx->pid = pid; hctx->fd = from_cgi_fds[0]; hctx->fde_ndx = -1; ++srv->cur_fds; if (0 == con->request.content_length) { close(to_cgi_fds[1]); } else { /* there is content to send */ if (-1 == fdevent_fcntl_set_nb(srv->ev, to_cgi_fds[1])) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); close(to_cgi_fds[1]); cgi_connection_close(srv, hctx); return -1; } if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) { close(to_cgi_fds[1]); cgi_connection_close(srv, hctx); return -1; } ++srv->cur_fds; } fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); if (-1 == fdevent_fcntl_set_nb(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); cgi_connection_close(srv, hctx); return -1; } fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); break; } } return 0; #else return -1; #endif }
handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) { #ifdef HAVE_FAM_H fam_dir_entry *fam_dir = NULL; int dir_ndx = -1; splay_tree *dir_node = NULL; #endif stat_cache_entry *sce = NULL; stat_cache *sc; struct stat st; size_t k; int fd; struct stat lst; #ifdef DEBUG_STAT_CACHE size_t i; #endif int file_ndx; splay_tree *file_node = NULL; *ret_sce = NULL; /* * check if the directory for this file has changed */ sc = srv->stat_cache; buffer_copy_buffer(sc->hash_key, name); buffer_append_int(sc->hash_key, con->conf.follow_symlink); file_ndx = hashme(sc->hash_key); sc->files = splaytree_splay(sc->files, file_ndx); #ifdef DEBUG_STAT_CACHE for (i = 0; i < ctrl.used; i++) { if (ctrl.ptr[i] == file_ndx) break; } #endif if (sc->files && (sc->files->key == file_ndx)) { #ifdef DEBUG_STAT_CACHE /* it was in the cache */ force_assert(i < ctrl.used); #endif /* we have seen this file already and * don't stat() it again in the same second */ file_node = sc->files; sce = file_node->data; /* check if the name is the same, we might have a collision */ if (buffer_is_equal(name, sce->name)) { if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { if (sce->stat_ts == srv->cur_ts) { *ret_sce = sce; return HANDLER_GO_ON; } } } else { /* oops, a collision, * * file_node is used by the FAM check below to see if we know this file * and if we can save a stat(). * * BUT, the sce is not reset here as the entry into the cache is ok, we * it is just not pointing to our requested file. * * */ file_node = NULL; } } else { #ifdef DEBUG_STAT_CACHE if (i != ctrl.used) { log_error_write(srv, __FILE__, __LINE__, "xSB", file_ndx, "was already inserted but not found in cache, ", name); } force_assert(i == ctrl.used); #endif } #ifdef HAVE_FAM_H /* dir-check */ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (0 != buffer_copy_dirname(sc->dir_name, name)) { log_error_write(srv, __FILE__, __LINE__, "sb", "no '/' found in filename:", name); return HANDLER_ERROR; } buffer_copy_buffer(sc->hash_key, sc->dir_name); buffer_append_int(sc->hash_key, con->conf.follow_symlink); dir_ndx = hashme(sc->hash_key); sc->dirs = splaytree_splay(sc->dirs, dir_ndx); if (sc->dirs && (sc->dirs->key == dir_ndx)) { dir_node = sc->dirs; } if (dir_node && file_node) { /* we found a file */ sce = file_node->data; fam_dir = dir_node->data; if (fam_dir->version == sce->dir_version) { /* the stat()-cache entry is still ok */ *ret_sce = sce; return HANDLER_GO_ON; } } } #endif /* * *lol* * - open() + fstat() on a named-pipe results in a (intended) hang. * - stat() if regular file + open() to see if we can read from it is better * * */ if (-1 == stat(name->ptr, &st)) { return HANDLER_ERROR; } if (S_ISREG(st.st_mode)) { /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */ if (name->ptr[buffer_string_length(name) - 1] == '/') { errno = ENOTDIR; return HANDLER_ERROR; } /* try to open the file to check if we can read it */ if (-1 == (fd = open(name->ptr, O_RDONLY))) { return HANDLER_ERROR; } close(fd); } if (NULL == sce) { #ifdef DEBUG_STAT_CACHE int osize = splaytree_size(sc->files); #endif sce = stat_cache_entry_init(); buffer_copy_buffer(sce->name, name); sc->files = splaytree_insert(sc->files, file_ndx, sce); #ifdef DEBUG_STAT_CACHE if (ctrl.size == 0) { ctrl.size = 16; ctrl.used = 0; ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr)); } else if (ctrl.size == ctrl.used) { ctrl.size += 16; ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr)); } ctrl.ptr[ctrl.used++] = file_ndx; force_assert(sc->files); force_assert(sc->files->data == sce); force_assert(osize + 1 == splaytree_size(sc->files)); #endif } sce->st = st; sce->stat_ts = srv->cur_ts; /* catch the obvious symlinks * * this is not a secure check as we still have a race-condition between * the stat() and the open. We can only solve this by * 1. open() the file * 2. fstat() the fd * * and keeping the file open for the rest of the time. But this can * only be done at network level. * * per default it is not a symlink * */ #ifdef HAVE_LSTAT sce->is_symlink = 0; /* we want to only check for symlinks if we should block symlinks. */ if (!con->conf.follow_symlink) { if (stat_cache_lstat(srv, name, &lst) == 0) { #ifdef DEBUG_STAT_CACHE log_error_write(srv, __FILE__, __LINE__, "sb", "found symlink", name); #endif sce->is_symlink = 1; } /* * we assume "/" can not be symlink, so * skip the symlink stuff if our path is / **/ else if (buffer_string_length(name) > 1) { buffer *dname; char *s_cur; dname = buffer_init(); buffer_copy_buffer(dname, name); while ((s_cur = strrchr(dname->ptr, '/'))) { buffer_string_set_length(dname, s_cur - dname->ptr); if (dname->ptr == s_cur) { #ifdef DEBUG_STAT_CACHE log_error_write(srv, __FILE__, __LINE__, "s", "reached /"); #endif break; } #ifdef DEBUG_STAT_CACHE log_error_write(srv, __FILE__, __LINE__, "sbs", "checking if", dname, "is a symlink"); #endif if (stat_cache_lstat(srv, dname, &lst) == 0) { sce->is_symlink = 1; #ifdef DEBUG_STAT_CACHE log_error_write(srv, __FILE__, __LINE__, "sb", "found symlink", dname); #endif break; }; }; buffer_free(dname); }; }; #endif if (S_ISREG(st.st_mode)) { /* determine mimetype */ buffer_reset(sce->content_type); #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) if (con->conf.use_xattr) { stat_cache_attr_get(sce->content_type, name->ptr); } #endif /* xattr did not set a content-type. ask the config */ if (buffer_string_is_empty(sce->content_type)) { size_t namelen = buffer_string_length(name); for (k = 0; k < con->conf.mimetypes->used; k++) { data_string *ds = (data_string *)con->conf.mimetypes->data[k]; buffer *type = ds->key; size_t typelen = buffer_string_length(type); if (buffer_is_empty(type)) continue; /* check if the right side is the same */ if (typelen > namelen) continue; if (0 == strncasecmp(name->ptr + namelen - typelen, type->ptr, typelen)) { buffer_copy_buffer(sce->content_type, ds->value); break; } } } etag_create(sce->etag, &(sce->st), con->etag_flags); } else if (S_ISDIR(st.st_mode)) { etag_create(sce->etag, &(sce->st), con->etag_flags); } #ifdef HAVE_FAM_H if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { /* is this directory already registered ? */ if (!dir_node) { fam_dir = fam_dir_entry_init(); buffer_copy_buffer(fam_dir->name, sc->dir_name); fam_dir->version = 1; fam_dir->req = calloc(1, sizeof(FAMRequest)); if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr, fam_dir->req, fam_dir)) { log_error_write(srv, __FILE__, __LINE__, "sbsbs", "monitoring dir failed:", fam_dir->name, "file:", name, FamErrlist[FAMErrno]); fam_dir_entry_free(&sc->fam, fam_dir); fam_dir = NULL; } else { int osize = 0; if (sc->dirs) { osize = sc->dirs->size; } sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir); force_assert(sc->dirs); force_assert(sc->dirs->data == fam_dir); force_assert(osize == (sc->dirs->size - 1)); } } else { fam_dir = dir_node->data; } /* bind the fam_fc to the stat() cache entry */ if (fam_dir) { sce->dir_version = fam_dir->version; } } #endif *ret_sce = sce; return HANDLER_GO_ON; }
lua_State *script_cache_get_script(server *srv, connection *con, script_cache *cache, buffer *name) { size_t i; script *sc = NULL; stat_cache_entry *sce; for (i = 0; i < cache->used; i++) { sc = cache->ptr[i]; if (buffer_is_equal(name, sc->name)) { sc->last_used = time(NULL); /* oops, the script failed last time */ if (lua_gettop(sc->L) == 0) break; force_assert(lua_gettop(sc->L) == 1); if (HANDLER_ERROR == stat_cache_get_entry(srv, con, sc->name, &sce)) { lua_pop(sc->L, 1); /* pop the old function */ break; } if (!buffer_is_equal(sce->etag, sc->etag)) { /* the etag is outdated, reload the function */ lua_pop(sc->L, 1); break; } force_assert(lua_isfunction(sc->L, -1)); return sc->L; } sc = NULL; } /* if the script was script already loaded but either got changed or * failed to load last time */ if (sc == NULL) { sc = script_init(); if (cache->size == 0) { cache->size = 16; cache->ptr = malloc(cache->size * sizeof(*(cache->ptr))); } else if (cache->used == cache->size) { cache->size += 16; cache->ptr = realloc(cache->ptr, cache->size * sizeof(*(cache->ptr))); } cache->ptr[cache->used++] = sc; buffer_copy_buffer(sc->name, name); sc->L = luaL_newstate(); luaL_openlibs(sc->L); } sc->last_used = time(NULL); if (0 != luaL_loadfile(sc->L, name->ptr)) { /* oops, an error, return it */ return sc->L; } if (HANDLER_GO_ON == stat_cache_get_entry(srv, con, sc->name, &sce)) { buffer_copy_buffer(sc->etag, sce->etag); } force_assert(lua_isfunction(sc->L, -1)); return sc->L; }
/** * walk through the content array * * content = { "<pre>", { file = "/content" } , "</pre>" } * * header["Content-Type"] = "text/html" * * return 200 */ static int magnet_attach_content(server *srv, connection *con, lua_State *L, int lighty_table_ndx) { force_assert(lua_istable(L, lighty_table_ndx)); lua_getfield(L, lighty_table_ndx, "content"); /* lighty.content */ if (lua_istable(L, -1)) { int i; /* content is found, and is a table */ for (i = 1; ; i++) { lua_rawgeti(L, -1, i); /* -1 is the value and should be the value ... aka a table */ if (lua_isstring(L, -1)) { const_buffer data = magnet_checkconstbuffer(L, -1); chunkqueue_append_mem(con->write_queue, data.ptr, data.len); } else if (lua_istable(L, -1)) { lua_getfield(L, -1, "filename"); lua_getfield(L, -2, "length"); lua_getfield(L, -3, "offset"); if (lua_isstring(L, -3)) { /* filename has to be a string */ buffer *fn; stat_cache_entry *sce; handler_t res; fn = magnet_checkbuffer(L, -3); res = stat_cache_get_entry(srv, con, fn, &sce); if (HANDLER_GO_ON == res) { off_t off = (off_t) luaL_optinteger(L, -1, 0); off_t len = (off_t) luaL_optinteger(L, -2, (lua_Integer) sce->st.st_size); if (off < 0) { buffer_free(fn); return luaL_error(L, "offset for '%s' is negative", lua_tostring(L, -3)); } if (len < off) { buffer_free(fn); return luaL_error(L, "offset > length for '%s'", lua_tostring(L, -3)); } chunkqueue_append_file(con->write_queue, fn, off, len - off); } buffer_free(fn); } else { return luaL_error(L, "content[%d] is a table and requires the field \"filename\"", i); } lua_pop(L, 3); } else if (lua_isnil(L, -1)) { /* end of list */ lua_pop(L, 1); break; } else { return luaL_error(L, "content[%d] is neither a string nor a table: ", i); } lua_pop(L, 1); /* pop the content[...] entry value */ } } else { return luaL_error(L, "lighty.content has to be a table"); } lua_pop(L, 1); /* pop lighty.content */ return 0; }
static handler_t magnet_attract(server *srv, connection *con, plugin_data *p, buffer *name) { lua_State *L; int lua_return_value; const int func_ndx = 1; const int lighty_table_ndx = 2; /* get the script-context */ L = script_cache_get_script(srv, con, p->cache, name); if (lua_isstring(L, -1)) { log_error_write(srv, __FILE__, __LINE__, "sbss", "loading script", name, "failed:", lua_tostring(L, -1)); lua_pop(L, 1); force_assert(lua_gettop(L) == 0); /* only the error should have been on the stack */ con->http_status = 500; con->mode = DIRECT; return HANDLER_FINISHED; } force_assert(lua_gettop(L) == 1); force_assert(lua_isfunction(L, func_ndx)); lua_pushlightuserdata(L, srv); lua_setfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_SERVER); lua_pushlightuserdata(L, con); lua_setfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_CONNECTION); lua_atpanic(L, magnet_atpanic); /** * we want to create empty environment for our script * * setmetatable({}, {__index = _G}) * * if a function symbol is not defined in our env, __index will lookup * in the global env. * * all variables created in the script-env will be thrown * away at the end of the script run. */ lua_newtable(L); /* my empty environment aka {} (sp += 1) */ /* we have to overwrite the print function */ lua_pushcfunction(L, magnet_print); /* (sp += 1) */ lua_setfield(L, -2, "print"); /* -1 is the env we want to set(sp -= 1) */ /** * lighty.request[] (ro) has the HTTP-request headers * lighty.env[] (rw) has various url/physical file paths and * request meta data; might contain nil values * lighty.req_env[] (ro) has the cgi environment * lighty.status[] (ro) has the status counters * lighty.content[] (rw) is a table of string/file * lighty.header[] (rw) is a array to set response headers */ lua_newtable(L); /* lighty.* (sp += 1) */ lua_newtable(L); /* {} (sp += 1) */ lua_newtable(L); /* the meta-table for the request-table (sp += 1) */ lua_pushcfunction(L, magnet_reqhdr_get); /* (sp += 1) */ lua_setfield(L, -2, "__index"); /* (sp -= 1) */ lua_pushcfunction(L, magnet_reqhdr_pairs); /* (sp += 1) */ lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ lua_setmetatable(L, -2); /* tie the metatable to request (sp -= 1) */ lua_setfield(L, -2, "request"); /* content = {} (sp -= 1) */ lua_newtable(L); /* {} (sp += 1) */ lua_newtable(L); /* the meta-table for the env-table (sp += 1) */ lua_pushcfunction(L, magnet_env_get); /* (sp += 1) */ lua_setfield(L, -2, "__index"); /* (sp -= 1) */ lua_pushcfunction(L, magnet_env_set); /* (sp += 1) */ lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ lua_pushcfunction(L, magnet_env_pairs); /* (sp += 1) */ lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ lua_setmetatable(L, -2); /* tie the metatable to env (sp -= 1) */ lua_setfield(L, -2, "env"); /* content = {} (sp -= 1) */ lua_newtable(L); /* {} (sp += 1) */ lua_newtable(L); /* the meta-table for the req_env-table (sp += 1) */ lua_pushcfunction(L, magnet_cgi_get); /* (sp += 1) */ lua_setfield(L, -2, "__index"); /* (sp -= 1) */ lua_pushcfunction(L, magnet_cgi_set); /* (sp += 1) */ lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ lua_pushcfunction(L, magnet_cgi_pairs); /* (sp += 1) */ lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ lua_setmetatable(L, -2); /* tie the metatable to req_env (sp -= 1) */ lua_setfield(L, -2, "req_env"); /* content = {} (sp -= 1) */ lua_newtable(L); /* {} (sp += 1) */ lua_newtable(L); /* the meta-table for the status-table (sp += 1) */ lua_pushcfunction(L, magnet_status_get); /* (sp += 1) */ lua_setfield(L, -2, "__index"); /* (sp -= 1) */ lua_pushcfunction(L, magnet_status_set); /* (sp += 1) */ lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ lua_pushcfunction(L, magnet_status_pairs); /* (sp += 1) */ lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ lua_setmetatable(L, -2); /* tie the metatable to statzs (sp -= 1) */ lua_setfield(L, -2, "status"); /* content = {} (sp -= 1) */ /* add empty 'content' and 'header' tables */ lua_newtable(L); /* {} (sp += 1) */ lua_setfield(L, -2, "content"); /* content = {} (sp -= 1) */ lua_newtable(L); /* {} (sp += 1) */ lua_setfield(L, -2, "header"); /* header = {} (sp -= 1) */ lua_pushinteger(L, MAGNET_RESTART_REQUEST); lua_setfield(L, -2, "RESTART_REQUEST"); lua_pushcfunction(L, magnet_stat); /* (sp += 1) */ lua_setfield(L, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */ /* insert lighty table at index 2 */ lua_pushvalue(L, -1); lua_insert(L, lighty_table_ndx); lua_setfield(L, -2, "lighty"); /* lighty.* (sp -= 1) */ #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 /* override the default pairs() function to our __pairs capable version; * not needed for lua 5.2+ */ lua_getglobal(L, "pairs"); /* push original pairs() (sp += 1) */ lua_pushcclosure(L, magnet_pairs, 1); lua_setfield(L, -2, "pairs"); /* (sp -= 1) */ #endif lua_newtable(L); /* the meta-table for the new env (sp += 1) */ magnet_get_global_table(L); /* (sp += 1) */ lua_setfield(L, -2, "__index"); /* { __index = _G } (sp -= 1) */ lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */ magnet_setfenv_mainfn(L, 1); /* (sp -= 1) */ /* pcall will destroy the func value, duplicate it */ /* (sp += 1) */ lua_pushvalue(L, func_ndx); { int errfunc = push_traceback(L, 0); int ret = lua_pcall(L, 0, 1, errfunc); lua_remove(L, errfunc); /* reset environment */ magnet_get_global_table(L); /* (sp += 1) */ magnet_setfenv_mainfn(L, 1); /* (sp -= 1) */ if (0 != ret) { log_error_write(srv, __FILE__, __LINE__, "ss", "lua_pcall():", lua_tostring(L, -1)); lua_pop(L, 2); /* remove the error-msg and the lighty table at index 2 */ force_assert(lua_gettop(L) == 1); /* only the function should be on the stack */ con->http_status = 500; con->mode = DIRECT; return HANDLER_FINISHED; } } /* we should have the function, the lighty table and the return value on the stack */ force_assert(lua_gettop(L) == 3); lua_return_value = (int) luaL_optinteger(L, -1, -1); lua_pop(L, 1); /* pop return value */ magnet_copy_response_header(srv, con, L, lighty_table_ndx); { handler_t result = HANDLER_GO_ON; if (lua_return_value > 99) { con->http_status = lua_return_value; con->file_finished = 1; /* try { ...*/ if (0 == setjmp(exceptionjmp)) { magnet_attach_content(srv, con, L, lighty_table_ndx); if (!chunkqueue_is_empty(con->write_queue)) { con->mode = p->id; } } else { lua_settop(L, 2); /* remove all but function and lighty table */ /* } catch () { */ con->http_status = 500; con->mode = DIRECT; } result = HANDLER_FINISHED; } else if (MAGNET_RESTART_REQUEST == lua_return_value) { result = HANDLER_COMEBACK; } lua_pop(L, 1); /* pop the lighty table */ force_assert(lua_gettop(L) == 1); /* only the function should remain on the stack */ return result; } }
void chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs, unsigned int upload_temp_file_size) { force_assert(NULL != cq); cq->tempdirs = tempdirs; cq->upload_temp_file_size = upload_temp_file_size; }
static int load_next_chunk(server *srv, connection *con, chunkqueue *cq, off_t max_bytes, const char **data, size_t *data_len) { chunk * const c = cq->first; #define LOCAL_SEND_BUFSIZE (64 * 1024) /* this is a 64k sendbuffer * * it has to stay at the same location all the time to satisfy the needs * of SSL_write to pass the SAME parameter in case of a _WANT_WRITE * * the buffer is allocated once, is NOT realloced and is NOT freed at shutdown * -> we expect a 64k block to 'leak' in valgrind * */ static char *local_send_buffer = NULL; force_assert(NULL != c); switch (c->type) { case MEM_CHUNK: { size_t have; force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem)); have = buffer_string_length(c->mem) - c->offset; if ((off_t) have > max_bytes) have = max_bytes; *data = c->mem->ptr + c->offset; *data_len = have; } return 0; case FILE_CHUNK: if (NULL == local_send_buffer) { local_send_buffer = malloc(LOCAL_SEND_BUFSIZE); force_assert(NULL != local_send_buffer); } if (0 != network_open_file_chunk(srv, con, cq)) return -1; { off_t offset, toSend; force_assert(c->offset >= 0 && c->offset <= c->file.length); offset = c->file.start + c->offset; toSend = c->file.length - c->offset; if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE; if (toSend > max_bytes) toSend = max_bytes; if (-1 == lseek(c->file.fd, offset, SEEK_SET)) { log_error_write(srv, __FILE__, __LINE__, "ss", "lseek: ", strerror(errno)); return -1; } if (-1 == (toSend = read(c->file.fd, local_send_buffer, toSend))) { log_error_write(srv, __FILE__, __LINE__, "ss", "read: ", strerror(errno)); return -1; } *data = local_send_buffer; *data_len = toSend; } return 0; } return -1; }
handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) { size_t i; stat_cache *sc = srv->stat_cache; size_t events; UNUSED(_fce); /* */ if (revent & FDEVENT_IN) { events = FAMPending(&sc->fam); for (i = 0; i < events; i++) { FAMEvent fe; fam_dir_entry *fam_dir; splay_tree *node; int ndx, j; FAMNextEvent(&sc->fam, &fe); /* handle event */ switch(fe.code) { case FAMChanged: case FAMDeleted: case FAMMoved: /* if the filename is a directory remove the entry */ fam_dir = fe.userdata; fam_dir->version++; /* file/dir is still here */ if (fe.code == FAMChanged) break; /* we have 2 versions, follow and no-follow-symlink */ for (j = 0; j < 2; j++) { buffer_copy_string(sc->hash_key, fe.filename); buffer_append_int(sc->hash_key, j); ndx = hashme(sc->hash_key); sc->dirs = splaytree_splay(sc->dirs, ndx); node = sc->dirs; if (node && (node->key == ndx)) { int osize = splaytree_size(sc->dirs); fam_dir_entry_free(&sc->fam, node->data); sc->dirs = splaytree_delete(sc->dirs, ndx); force_assert(osize - 1 == splaytree_size(sc->dirs)); } } break; default: break; } } } if (revent & FDEVENT_HUP) { /* fam closed the connection */ fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(&sc->fam)); fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(&sc->fam)); FAMClose(&sc->fam); } return HANDLER_GO_ON; }