2
0
Fork 0
lighttpd2/src/plugin_core.c

1080 lines
29 KiB
C
Raw Normal View History

#include <lighttpd/base.h>
#include <lighttpd/plugin_core.h>
#include <lighttpd/profiler.h>
2008-09-18 07:14:57 +00:00
#include <sys/stat.h>
#include <fcntl.h>
static action* core_list(server *srv, plugin* p, value *val) {
action *a;
guint i;
UNUSED(p);
if (!val) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (val->type == VALUE_ACTION) {
a = val->data.val_action.action;
2008-08-06 18:46:42 +00:00
action_acquire(a);
return a;
}
if (val->type != VALUE_LIST) {
ERROR(srv, "expected list, got %s", value_type_string(val->type));
return NULL;
}
a = action_new_list();
for (i = 0; i < val->data.list->len; i++) {
value *oa = g_array_index(val->data.list, value*, i);
if (oa->type != VALUE_ACTION) {
ERROR(srv, "expected action at entry %u of list, got %s", i, value_type_string(oa->type));
action_release(srv, a);
return NULL;
}
assert(srv == oa->data.val_action.srv);
action_acquire(oa->data.val_action.action);
g_array_append_val(a->data.list, oa->data.val_action.action);
}
return a;
}
static action* core_when(server *srv, plugin* p, value *val) {
value *val_cond, *val_act, *val_act_else;
action *a, *act = NULL, *act_else = NULL;
UNUSED(p);
if (!val) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (val->type != VALUE_LIST) {
ERROR(srv, "expected list, got %s", value_type_string(val->type));
return NULL;
}
if (val->data.list->len == 2) {
val_act_else = NULL;
act_else = NULL;
} else if (val->data.list->len == 3) {
val_act_else = g_array_index(val->data.list, value*, 2);
} else {
ERROR(srv, "expected list with length 2 or 3, has length %u", val->data.list->len);
return NULL;
}
val_cond = g_array_index(val->data.list, value*, 0);
val_act = g_array_index(val->data.list, value*, 1);
if (val_cond->type != VALUE_CONDITION) {
ERROR(srv, "expected condition as first parameter, got %s", value_type_string(val_cond->type));
return NULL;
}
if (val_act->type == VALUE_NONE) {
act = NULL;
} else if (val_act->type == VALUE_ACTION) {
act = val_act->data.val_action.action;
} else {
ERROR(srv, "expected action as second parameter, got %s", value_type_string(val_act->type));
return NULL;
}
if (val_act_else) {
if (val_act_else->type == VALUE_NONE) {
act_else = NULL;
} else if (val_act_else->type == VALUE_ACTION) {
act_else = val_act_else->data.val_action.action;
} else {
ERROR(srv, "expected action as third parameter, got %s", value_type_string(val_act_else->type));
return NULL;
}
}
condition_acquire(val_cond->data.val_cond.cond);
if (act) action_acquire(act);
if (act_else) action_acquire(act_else);
a = action_new_condition(val_cond->data.val_cond.cond, act, act_else);
return a;
}
static action* core_set(server *srv, plugin* p, value *val) {
value *val_val, *val_name;
action *a;
UNUSED(p);
if (!val) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (val->type != VALUE_LIST) {
ERROR(srv, "expected list, got %s", value_type_string(val->type));
return NULL;
}
if (val->data.list->len != 2) {
ERROR(srv, "expected list with length 2, has length %u", val->data.list->len);
return NULL;
}
val_name = g_array_index(val->data.list, value*, 0);
val_val = g_array_index(val->data.list, value*, 1);
if (val_name->type != VALUE_STRING) {
ERROR(srv, "expected string as first parameter, got %s", value_type_string(val_name->type));
return NULL;
}
a = option_action(srv, val_name->data.string->str, val_val);
return a;
}
static gboolean core_setup_set(server *srv, plugin* p, value *val) {
value *val_val, *val_name;
UNUSED(p);
if (!val) {
ERROR(srv, "%s", "need parameter");
return FALSE;
}
if (val->type != VALUE_LIST) {
ERROR(srv, "expected list, got %s", value_type_string(val->type));
return FALSE;
}
if (val->data.list->len != 2) {
ERROR(srv, "expected list with length 2, has length %u", val->data.list->len);
return FALSE;
}
val_name = g_array_index(val->data.list, value*, 0);
val_val = g_array_index(val->data.list, value*, 1);
if (val_name->type != VALUE_STRING) {
ERROR(srv, "expected string as first parameter, got %s", value_type_string(val_name->type));
return FALSE;
}
return plugin_set_default_option(srv, val_name->data.string->str, val_val);
}
static handler_t core_handle_docroot(vrequest *vr, gpointer param, gpointer *context) {
2008-12-09 23:23:27 +00:00
UNUSED(context);
g_string_truncate(vr->physical.doc_root, 0);
g_string_append_len(vr->physical.doc_root, GSTR_LEN((GString*) param));
/* reset stat info because path has changed */
vr->physical.have_stat = FALSE;
vr->physical.have_errno = FALSE;
VR_DEBUG(vr, "docroot: %s", vr->physical.doc_root->str);
2009-01-12 00:02:39 +00:00
/* build physical path: docroot + uri.path */
g_string_truncate(vr->physical.path, 0);
g_string_append_len(vr->physical.path, GSTR_LEN(vr->physical.doc_root));
g_string_append_len(vr->physical.path, GSTR_LEN(vr->request.uri.path));
VR_DEBUG(vr, "physical path: %s", vr->physical.path->str);
return HANDLER_GO_ON;
}
static void core_docroot_free(server *srv, gpointer param) {
UNUSED(srv);
g_string_free(param, TRUE);
}
static action* core_docroot(server *srv, plugin* p, value *val) {
UNUSED(p);
if (!val || val->type != VALUE_STRING) {
ERROR(srv, "%s", "docroot action expects a string parameter");
return NULL;
}
return action_new_function(core_handle_docroot, NULL, core_docroot_free, value_extract(val).string);
}
static handler_t core_handle_static(vrequest *vr, gpointer param, gpointer *context) {
int fd;
2009-03-26 22:05:17 +00:00
struct stat st;
int err;
handler_t res;
2009-03-01 20:16:58 +00:00
UNUSED(param);
UNUSED(context);
switch (vr->request.http_method) {
case HTTP_METHOD_GET:
case HTTP_METHOD_HEAD:
break;
default:
return HANDLER_GO_ON;
}
if (vrequest_is_handled(vr)) return HANDLER_GO_ON;
2009-03-26 22:05:17 +00:00
if (vr->physical.path->len == 0) return HANDLER_GO_ON;
2009-03-26 22:05:17 +00:00
res = stat_cache_get(vr, vr->physical.path, &st, &err, &fd);
if (res == HANDLER_WAIT_FOR_EVENT)
return res;
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "try serving static file: '%s'", vr->physical.path->str);
}
2009-03-26 22:05:17 +00:00
if (res == HANDLER_ERROR) {
/* open or fstat failed */
2009-03-26 22:33:28 +00:00
if (fd != -1)
close(fd);
2009-03-26 22:05:17 +00:00
if (!vrequest_handle_direct(vr)) {
return HANDLER_ERROR;
}
2009-03-01 20:16:58 +00:00
2009-03-26 22:05:17 +00:00
switch (err) {
2009-03-01 20:16:58 +00:00
case ENOENT:
case ENOTDIR:
2009-03-26 22:05:17 +00:00
vr->response.http_status = 404;
return HANDLER_GO_ON;
2009-03-01 20:16:58 +00:00
case EACCES:
vr->response.http_status = 403;
return HANDLER_GO_ON;
2009-03-01 20:16:58 +00:00
default:
2009-03-26 22:05:17 +00:00
VR_ERROR(vr, "stat() or open() for '%s' failed: %s", vr->physical.path->str, g_strerror(err));
return HANDLER_ERROR;
}
2009-03-26 22:05:17 +00:00
} else if (S_ISDIR(st.st_mode)) {
2009-03-26 22:33:28 +00:00
if (fd != -1)
close(fd);
return HANDLER_GO_ON;
2009-03-26 22:05:17 +00:00
} else if (!S_ISREG(st.st_mode)) {
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "not a regular file: '%s'", vr->physical.path->str);
2009-03-01 20:16:58 +00:00
}
2009-03-26 22:33:28 +00:00
if (fd != -1)
close(fd);
if (!vrequest_handle_direct(vr)) {
return HANDLER_ERROR;
}
vr->response.http_status = 403;
} else {
GString *mime_str;
gboolean cachable;
#ifdef FD_CLOEXEC
fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
if (!vrequest_handle_direct(vr)) {
close(fd);
return HANDLER_ERROR;
}
2009-03-26 22:05:17 +00:00
etag_set_header(vr, &st, &cachable);
if (cachable) {
vr->response.http_status = 304;
close(fd);
return HANDLER_GO_ON;
}
mime_str = mimetype_get(vr, vr->request.uri.path);
vr->response.http_status = 200;
if (mime_str)
http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), GSTR_LEN(mime_str));
else
http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));
2009-03-26 22:05:17 +00:00
chunkqueue_append_file_fd(vr->out, NULL, 0, st.st_size, fd);
}
return HANDLER_GO_ON;
}
static action* core_static(server *srv, plugin* p, value *val) {
UNUSED(p);
if (val) {
ERROR(srv, "%s", "static action doesn't have parameters");
return NULL;
}
2008-12-09 23:23:27 +00:00
return action_new_function(core_handle_static, NULL, NULL, NULL);
}
2009-03-06 18:19:36 +00:00
static void core_log_write_free(server *srv, gpointer param) {
UNUSED(srv);
g_string_free(param, TRUE);
}
static handler_t core_handle_log_write(vrequest *vr, gpointer param, gpointer *context) {
GString *msg = param;
UNUSED(context);
VR_INFO(vr, "%s", msg->str);
return HANDLER_GO_ON;
}
static action* core_log_write(server *srv, plugin* p, value *val) {
UNUSED(p);
if (!val || val->type != VALUE_STRING) {
ERROR(srv, "%s", "log.write expects a string parameter");
return NULL;
}
2009-03-06 18:19:36 +00:00
return action_new_function(core_handle_log_write, NULL, core_log_write_free, value_extract(val).string);
}
2008-08-06 18:46:42 +00:00
2008-12-09 23:23:27 +00:00
static handler_t core_handle_blank(vrequest *vr, gpointer param, gpointer *context) {
2008-08-16 22:23:52 +00:00
UNUSED(param);
2008-12-09 23:23:27 +00:00
UNUSED(context);
2008-08-16 22:23:52 +00:00
if (!vrequest_handle_direct(vr)) return HANDLER_GO_ON;
2008-08-16 22:23:52 +00:00
vr->response.http_status = 200;
2008-08-16 22:23:52 +00:00
return HANDLER_GO_ON;
2008-08-16 22:23:52 +00:00
}
static action* core_blank(server *srv, plugin* p, value *val) {
2008-08-16 22:23:52 +00:00
UNUSED(p);
if (val) {
ERROR(srv, "%s", "'blank' action doesn't have parameters");
2008-08-16 22:23:52 +00:00
return NULL;
}
2008-12-09 23:23:27 +00:00
return action_new_function(core_handle_blank, NULL, NULL, NULL);
2008-08-16 22:23:52 +00:00
}
2008-12-09 23:23:27 +00:00
static handler_t core_handle_profile_mem(vrequest *vr, gpointer param, gpointer *context) {
2008-10-25 16:01:02 +00:00
UNUSED(vr);
UNUSED(param);
2008-12-09 23:23:27 +00:00
UNUSED(context);
/*g_mem_profile();*/
profiler_dump();
2008-10-25 16:01:02 +00:00
return HANDLER_GO_ON;
}
static action* core_profile_mem(server *srv, plugin* p, value *val) {
UNUSED(p);
if (val) {
ERROR(srv, "%s", "'profile_mem' action doesn't have parameters");
return NULL;
}
2008-12-09 23:23:27 +00:00
return action_new_function(core_handle_profile_mem, NULL, NULL, NULL);
}
static gboolean core_listen(server *srv, plugin* p, value *val) {
GString *ipstr;
2008-12-20 15:25:02 +00:00
int s;
UNUSED(p);
if (val->type != VALUE_STRING) {
ERROR(srv, "%s", "listen expects a string as parameter");
return FALSE;
}
2008-12-20 15:25:02 +00:00
ipstr = val->data.string;
if (-1 == (s = angel_listen(srv, ipstr))) {
ERROR(srv, "%s", "angel_listen failed");
2008-08-06 18:46:42 +00:00
return FALSE;
}
2008-12-20 15:25:02 +00:00
server_listen(srv, s);
return TRUE;
}
static gboolean core_event_handler(server *srv, plugin* p, value *val) {
guint backend;
gchar *str;
UNUSED(p);
if (val->type != VALUE_STRING) {
ERROR(srv, "%s", "event_handler expects a string as parameter");
return FALSE;
}
str = val->data.string->str;
backend = 0; /* libev will chose the right one by default */
if (g_str_equal(str, "select"))
backend = EVBACKEND_SELECT;
else if (g_str_equal(str, "poll"))
backend = EVBACKEND_POLL;
else if (g_str_equal(str, "epoll"))
backend = EVBACKEND_EPOLL;
else if (g_str_equal(str, "kqueue"))
backend = EVBACKEND_KQUEUE;
else if (g_str_equal(str, "devpoll"))
backend = EVBACKEND_DEVPOLL;
else if (g_str_equal(str, "port"))
backend = EVBACKEND_PORT;
else {
ERROR(srv, "unkown event handler: '%s'", str);
return FALSE;
}
if (backend) {
if (!(ev_supported_backends() & backend)) {
ERROR(srv, "unsupported event handler: '%s'", str);
return FALSE;
}
if (!(ev_recommended_backends() & backend)) {
DEBUG(srv, "warning: event handler '%s' not recommended for this platform!", str);
}
}
srv->loop_flags |= backend;
return TRUE;
}
static gboolean core_workers(server *srv, plugin* p, value *val) {
2008-09-09 09:31:08 +00:00
gint workers;
UNUSED(p);
workers = val->data.number;
if (val->type != VALUE_NUMBER || workers < 1) {
2008-09-09 09:31:08 +00:00
ERROR(srv, "%s", "workers expects a positive integer as parameter");
return FALSE;
}
if (srv->worker_count != 0) {
ERROR(srv, "workers already called with '%i', overwriting", srv->worker_count);
}
srv->worker_count = workers;
return TRUE;
}
2008-10-22 14:54:44 +00:00
static gboolean core_module_load(server *srv, plugin* p, value *val) {
value *mods = value_new_list();
UNUSED(p);
if (!g_module_supported()) {
ERROR(srv, "%s", "module loading not supported on this platform");
value_free(mods);
return FALSE;
}
if (val->type == VALUE_STRING) {
/* load only one module */
value *name = value_new_string(value_extract(val).string);
g_array_append_val(mods->data.list, name);
} else if (val->type == VALUE_LIST) {
/* load a list of modules */
for (guint i = 0; i < val->data.list->len; i++) {
value *v = g_array_index(val->data.list, value*, i);
if (v->type != VALUE_STRING) {
ERROR(srv, "module_load takes either a string or a list of strings as parameter, list with %s entry given", value_type_string(v->type));
value_free(mods);
return FALSE;
}
}
2009-03-06 17:52:48 +00:00
g_array_free(mods->data.list, TRUE);
2008-10-22 14:54:44 +00:00
mods->data.list = value_extract(val).list;
} else {
ERROR(srv, "module_load takes either a string or a list of strings as parameter, %s given", value_type_string(val->type));
return FALSE;
}
/* parameter types ok, load modules */
for (guint i = 0; i < mods->data.list->len; i++) {
GString *name = g_array_index(mods->data.list, value*, i)->data.string;
if (!module_load(srv->modules, name->str)) {
ERROR(srv, "could not load module '%s': %s", name->str, g_module_error());
value_free(mods);
return FALSE;
}
DEBUG(srv, "loaded module '%s'", name->str);
2008-10-22 14:54:44 +00:00
}
value_free(mods);
return TRUE;
}
2008-11-10 14:39:03 +00:00
static gboolean core_io_timeout(server *srv, plugin* p, value *val) {
UNUSED(p);
if (!val || val->type != VALUE_NUMBER || val->data.number < 1) {
ERROR(srv, "%s", "io_timeout expects a positive number as parameter");
return FALSE;
}
srv->io_timeout = value_extract(val).number;
return TRUE;
}
static gboolean core_stat_cache_ttl(server *srv, plugin* p, value *val) {
UNUSED(p);
if (!val || val->type != VALUE_NUMBER || val->data.number < 1) {
ERROR(srv, "%s", "stat_cache.ttl expects a positive number as parameter");
return FALSE;
}
srv->stat_cache_ttl = (gdouble)value_extract(val).number;
return TRUE;
}
2008-10-22 14:54:44 +00:00
/*
* OPTIONS
*/
static gboolean core_option_log_parse(server *srv, plugin *p, size_t ndx, value *val, option_value *oval) {
GHashTableIter iter;
gpointer k, v;
log_level_t level;
GString *path;
GString *level_str;
2008-12-31 01:50:32 +00:00
GArray *arr = g_array_sized_new(FALSE, TRUE, sizeof(log_t*), 6);
UNUSED(p);
UNUSED(ndx);
oval->list = arr;
2008-12-31 01:50:32 +00:00
g_array_set_size(arr, 6);
/* default value */
if (!val) {
2008-12-31 01:50:32 +00:00
/* default: log LOG_LEVEL_WARNING, LOG_LEVEL_ERROR and LOG_LEVEL_BACKEND to stderr */
log_t *log = srv->logs.stderr;
log_ref(srv, log);
g_array_index(arr, log_t*, LOG_LEVEL_WARNING) = log;
log_ref(srv, log);
g_array_index(arr, log_t*, LOG_LEVEL_ERROR) = log;
2008-12-31 01:50:32 +00:00
log_ref(srv, log);
g_array_index(arr, log_t*, LOG_LEVEL_BACKEND) = log;
return TRUE;
}
g_hash_table_iter_init(&iter, val->data.hash);
while (g_hash_table_iter_next(&iter, &k, &v)) {
if (((value*)v)->type != VALUE_STRING) {
ERROR(srv, "log expects a hashtable with string values, %s given", value_type_string(((value*)v)->type));
g_array_free(arr, TRUE);
return FALSE;
}
path = ((value*)v)->data.string;
level_str = (GString*)k;
if (g_str_equal(level_str->str, "*")) {
for (guint i = 0; i < arr->len; i++) {
log_t *log;
if (NULL != g_array_index(arr, log_t*, i))
continue;
log = log_new(srv, log_type_from_path(path), path);
g_array_index(arr, log_t*, i) = log;
}
}
else {
log_t *log = log_new(srv, log_type_from_path(path), path);
level = log_level_from_string(level_str);
g_array_index(arr, log_t*, level) = log;
}
}
return TRUE;
}
static void core_option_log_free(server *srv, plugin *p, size_t ndx, option_value oval) {
GArray *arr = oval.list;
UNUSED(p);
UNUSED(ndx);
if (!arr) return;
for (guint i = 0; i < arr->len; i++) {
if (NULL != g_array_index(arr, log_t*, i))
log_unref(srv, g_array_index(arr, log_t*, i));
}
g_array_free(arr, TRUE);
}
static gboolean core_option_log_timestamp_parse(server *srv, plugin *p, size_t ndx, value *val, option_value *oval) {
UNUSED(p);
UNUSED(ndx);
if (!val) return TRUE;
oval->ptr = log_timestamp_new(srv, value_extract(val).string);
return TRUE;
}
static void core_option_log_timestamp_free(server *srv, plugin *p, size_t ndx, option_value oval) {
UNUSED(p);
UNUSED(ndx);
if (!oval.ptr) return;
log_timestamp_free(srv, oval.ptr);
}
static gboolean core_option_mime_types_parse(server *srv, plugin *p, size_t ndx, value *val, option_value *oval) {
2008-12-09 23:23:27 +00:00
GArray *arr;
UNUSED(srv);
UNUSED(p);
UNUSED(ndx);
/* default value */
if (!val) {
oval->list = g_array_new(FALSE, TRUE, sizeof(value));
return TRUE;
}
/* check if the passed val is of type (("a", "b"), ("x", y")) */
arr = val->data.list;
for (guint i = 0; i < arr->len; i++) {
value *v = g_array_index(arr, value*, i);
value *v1, *v2;
if (v->type != VALUE_LIST) {
ERROR(srv, "mime_types option expects a list of string tuples, entry #%u is of type %s", i, value_type_string(v->type));
return FALSE;
}
if (v->data.list->len != 2) {
ERROR(srv, "mime_types option expects a list of string tuples, entry #%u is not a tuple", i);
return FALSE;
}
v1 = g_array_index(v->data.list, value*, 0);
v2 = g_array_index(v->data.list, value*, 1);
if (v1->type != VALUE_STRING || v2->type != VALUE_STRING) {
ERROR(srv, "mime_types option expects a list of string tuples, entry #%u is a (%s,%s) tuple", i, value_type_string(v1->type), value_type_string(v2->type));
return FALSE;
}
}
/* everything ok */
oval->list = value_extract(val).list;
return TRUE;
}
static void core_option_mime_types_free(server *srv, plugin *p, size_t ndx, option_value oval) {
UNUSED(srv);
UNUSED(p);
UNUSED(ndx);
for (guint i = 0; i < oval.list->len; i++)
value_free(g_array_index(oval.list, value*, i));
g_array_free(oval.list, TRUE);
}
static gboolean core_option_etag_use_parse(server *srv, plugin *p, size_t ndx, value *val, option_value *oval) {
GArray *arr;
guint flags = 0;
UNUSED(p);
UNUSED(ndx);
/* default value */
if (!val) {
oval->number = ETAG_USE_INODE | ETAG_USE_MTIME | ETAG_USE_SIZE;
return TRUE;
}
if (val->type != VALUE_LIST) {
ERROR(srv, "etag.use option expects a list of strings, parameter is of type %s", value_type_string(val->type));
}
arr = val->data.list;
for (guint i = 0; i < arr->len; i++) {
value *v = g_array_index(arr, value*, i);
if (v->type != VALUE_STRING) {
ERROR(srv, "etag.use option expects a list of strings, entry #%u is of type %s", i, value_type_string(v->type));
return FALSE;
}
if (0 == strcmp(v->data.string->str, "inode")) {
flags |= ETAG_USE_INODE;
} else if (0 == strcmp(v->data.string->str, "mtime")) {
flags |= ETAG_USE_MTIME;
} else if (0 == strcmp(v->data.string->str, "size")) {
flags |= ETAG_USE_SIZE;
} else {
ERROR(srv, "unknown etag.use flag: %s", v->data.string->str);
return FALSE;
}
}
oval->number = (guint64) flags;
return TRUE;
}
2008-12-09 23:23:27 +00:00
static handler_t core_handle_header_add(vrequest *vr, gpointer param, gpointer *context) {
GArray *l = (GArray*)param;
GString *k = g_array_index(l, value*, 0)->data.string;
GString *v = g_array_index(l, value*, 1)->data.string;
2008-12-09 23:23:27 +00:00
UNUSED(param);
UNUSED(context);
2008-10-25 16:01:02 +00:00
http_header_insert(vr->response.headers, GSTR_LEN(k), GSTR_LEN(v));
2008-10-25 16:01:02 +00:00
return HANDLER_GO_ON;
}
static void core_header_free(struct server *srv, gpointer param) {
UNUSED(srv);
value_list_free(param);
}
static action* core_header_add(server *srv, plugin* p, value *val) {
GArray *l;
UNUSED(p);
if (val->type != VALUE_LIST) {
ERROR(srv, "'core_header_add' action expects a string tuple as parameter, %s given", value_type_string(val->type));
return NULL;
}
l = val->data.list;
if (l->len != 2) {
ERROR(srv, "'core_header_add' action expects a string tuple as parameter, list has %u entries", l->len);
return NULL;
}
if (g_array_index(l, value*, 0)->type != VALUE_STRING || g_array_index(l, value*, 0)->type != VALUE_STRING) {