示例#1
0
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);
}
示例#2
0
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;
}
示例#3
0
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++;
	}
}
示例#4
0
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;
}
示例#5
0
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;
}
示例#6
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;
    }
}
示例#7
0
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;
}
示例#8
0
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;
}
示例#9
0
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;
}
示例#10
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;
}
示例#11
0
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;
    }
}
示例#12
0
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;
}
示例#13
0
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;
}
示例#14
0
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;
}
示例#15
0
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;
}
示例#16
0
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;
}
示例#17
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;
}
示例#18
0
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;
}
示例#19
0
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;
}
示例#20
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;
}
示例#21
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;
}
示例#22
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;
}
示例#23
0
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
}
示例#24
0
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;
}
示例#25
0
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;
}
示例#26
0
/**
 * 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;
}
示例#27
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;
	}
}
示例#28
0
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;
}
示例#29
0
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;
}
示例#30
0
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;
}