diff --git a/src/base.h b/src/base.h index 84d810f..49ee5e3 100644 --- a/src/base.h +++ b/src/base.h @@ -37,6 +37,8 @@ typedef struct connection connection; #include "connection.h" +#include "plugin_core.h" + #define SERVER_VERSION ((guint) 0x01FF0000) #endif diff --git a/src/config_parser.rl b/src/config_parser.rl index 6a6220b..1851224 100644 --- a/src/config_parser.rl +++ b/src/config_parser.rl @@ -509,10 +509,57 @@ /* create condition lvalue */ str = n->value.opt_string->str; - if (g_str_equal(str, "req.path") || g_str_equal(str, "request.path")) - lvalue = condition_lvalue_new(COMP_REQUEST_PATH, NULL); + + if (g_str_has_prefix(str, "req")) { + str += 3; + if (g_str_has_prefix(str, ".")) + str++; + else if (g_str_has_prefix(str, "uest.")) + str += 5; + else { + log_warning(srv, NULL, "unkown lvalue for condition: %s", n->value.opt_string->str); + return FALSE; + } + + if (g_str_equal(str, "host")) + lvalue = condition_lvalue_new(COMP_REQUEST_HOST, NULL); + else if (g_str_equal(str, "path")) + lvalue = condition_lvalue_new(COMP_REQUEST_PATH, NULL); + else if (g_str_equal(str, "query")) + lvalue = condition_lvalue_new(COMP_REQUEST_QUERY_STRING, NULL); + else if (g_str_equal(str, "method")) + lvalue = condition_lvalue_new(COMP_REQUEST_METHOD, NULL); + else if (g_str_equal(str, "scheme")) + lvalue = condition_lvalue_new(COMP_REQUEST_SCHEME, NULL); + else { + log_warning(srv, NULL, "unkown lvalue for condition: %s", n->value.opt_string->str); + return FALSE; + } + } + else if (g_str_has_prefix(str, "phys")) { + str += 3; + if (g_str_has_prefix(str, ".")) + str++; + else if (g_str_has_prefix(str, "ical.")) + str += 5; + else { + log_warning(srv, NULL, "unkown lvalue for condition: %s", n->value.opt_string->str); + return FALSE; + } + + if (g_str_equal(str, "path")) + lvalue = condition_lvalue_new(COMP_PHYSICAL_PATH, NULL); + else if (g_str_equal(str, "exists")) + lvalue = condition_lvalue_new(COMP_PHYSICAL_PATH_EXISTS, NULL); + else if (g_str_equal(str, "size")) + lvalue = condition_lvalue_new(COMP_PHYSICAL_SIZE, NULL); + else { + log_warning(srv, NULL, "unkown lvalue for condition: %s", n->value.opt_string->str); + return FALSE; + } + } else { - log_warning(srv, NULL, "unkown lvalue for condition: %s", str); + log_warning(srv, NULL, "unkown lvalue for condition: %s", n->value.opt_string->str); return FALSE; } diff --git a/src/log.c b/src/log.c index ff7800c..d783a59 100644 --- a/src/log.c +++ b/src/log.c @@ -39,8 +39,8 @@ gboolean log_write_(server *srv, connection *con, log_level_t log_level, const g if (con != NULL) { /* get log index from connection */ - log = con->log; - log_level_want = con->log_level; + log = CORE_OPTION(CORE_OPTION_LOG_TARGET) ? CORE_OPTION(CORE_OPTION_LOG_TARGET) : srv->log_stderr; + log_level_want = (log_level_t) CORE_OPTION(CORE_OPTION_LOG_LEVEL); } else { log = srv->log_stderr; @@ -202,6 +202,56 @@ void log_unref(server *srv, log_t *log) { g_mutex_unlock(srv->log_mutex); } +log_type_t log_type_from_path(GString *path) { + if (path->len == 0) + return LOG_TYPE_STDERR; + + /* targets starting with a slash are absolute paths and therefor file targets */ + if (*path->str == '/') + return LOG_TYPE_FILE; + + /* targets starting with a pipe are ... pipes! */ + if (*path->str == '|') + return LOG_TYPE_PIPE; + + if (g_str_equal(path->str, "stderr")) + return LOG_TYPE_STDERR; + + if (g_str_equal(path->str, "syslog")) + return LOG_TYPE_SYSLOG; + + /* fall back to stderr */ + return LOG_TYPE_STDERR; +} + +log_level_t log_level_from_string(GString *str) { + if (g_str_equal(str->str, "debug")) + return LOG_LEVEL_DEBUG; + if (g_str_equal(str->str, "info")) + return LOG_LEVEL_INFO; + if (g_str_equal(str->str, "message")) + return LOG_LEVEL_MESSAGE; + if (g_str_equal(str->str, "warning")) + return LOG_LEVEL_WARNING; + if (g_str_equal(str->str, "error")) + return LOG_LEVEL_ERROR; + + /* fall back to debug level */ + return LOG_LEVEL_DEBUG; +} + +gchar* log_level_str(log_level_t log_level) { + switch (log_level) { + case LOG_LEVEL_DEBUG: return "debug"; + case LOG_LEVEL_INFO: return "info"; + case LOG_LEVEL_MESSAGE: return "message"; + case LOG_LEVEL_WARNING: return "warning"; + case LOG_LEVEL_ERROR: return "error"; + default: return "unknown"; + } +} + + log_t *log_new(server *srv, log_type_t type, GString *path) { log_t *log; gint fd = -1; diff --git a/src/log.h b/src/log.h index 8403d88..8341f64 100644 --- a/src/log.h +++ b/src/log.h @@ -105,6 +105,11 @@ struct log_entry_t { GString *msg; }; +/* determines the type of a log target by the path given. /absolute/path = file; |app = pipe; stderr = stderr; syslog = syslog */ +log_type_t log_type_from_path(GString *path); + +log_level_t log_level_from_string(GString *str); +gchar* log_level_str(log_level_t log_level); /* log_new is used to create a new log target, if a log with the same path already exists, it is referenced instead */ log_t *log_new(server *srv, log_type_t type, GString *path); diff --git a/src/plugin.c b/src/plugin.c index f5a88f3..7f22fb7 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -173,7 +173,7 @@ gboolean parse_option(server *srv, const char *name, option *opt, option_set *ma return FALSE; } - if (sopt->type != opt->type) { + if (sopt->type != opt->type && sopt->type != OPTION_NONE) { ERROR(srv, "Unexpected option type '%s', expected '%s' for option %s", option_type_string(opt->type), option_type_string(sopt->type), name); return FALSE; diff --git a/src/plugin_core.c b/src/plugin_core.c index 9b7e2ff..8d2a8df 100644 --- a/src/plugin_core.c +++ b/src/plugin_core.c @@ -181,11 +181,13 @@ static action_result core_handle_test(server *srv, connection *con, gpointer par if (con->state != CON_STATE_HANDLE_REQUEST_HEADER) return ACTION_GO_ON; con->response.http_status = 200; - chunkqueue_append_mem(con->out, CONST_STR_LEN("path: ")); + chunkqueue_append_mem(con->out, CONST_STR_LEN("host: ")); + chunkqueue_append_mem(con->out, GSTR_LEN(con->request.uri.host)); + chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\npath: ")); chunkqueue_append_mem(con->out, GSTR_LEN(con->request.uri.path)); chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\nquery: ")); chunkqueue_append_mem(con->out, GSTR_LEN(con->request.uri.query)); - chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n\r\n--- Headers ---\r\n")); + chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n\r\n--- headers ---\r\n")); g_hash_table_iter_init(&iter, con->request.headers->table); while (g_hash_table_iter_next(&iter, &k, &v)) { hv = g_queue_peek_head_link(&((http_header*)v)->values); @@ -200,6 +202,11 @@ static action_result core_handle_test(server *srv, connection *con, gpointer par chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n")); connection_handle_direct(srv, con); + log_debug(srv, con, "core_handle_test: %s%s%s log_level: %s", + con->request.uri.path->str, con->request.uri.query->len ? "?" : "", con->request.uri.query->len ? con->request.uri.query->str : "", + log_level_str((log_level_t)CORE_OPTION(CORE_OPTION_LOG_LEVEL)) + ); + return ACTION_GO_ON; } @@ -268,9 +275,60 @@ static gboolean core_listen(server *srv, plugin* p, option *opt) { return TRUE; } + +gboolean core_option_log_target_parse(server *srv, plugin *p, size_t ndx, option *opt, gpointer *value) { + log_t *log; + log_type_t log_type; + + UNUSED(log); + UNUSED(log_type); + UNUSED(p); + + assert(opt->type == OPTION_STRING); + + log_type = log_type_from_path(opt->value.opt_string); + log = log_new(srv, log_type, opt->value.opt_string); + + *value = (gpointer)log; + + TRACE(srv, "log.target assignment; ndx: %zd, value: %s", ndx, opt->value.opt_string->str); + + return TRUE; +} + +void core_option_log_target_free(server *srv, plugin *p, size_t ndx, gpointer value) { + UNUSED(srv); + UNUSED(p); + UNUSED(ndx); + UNUSED(value); +} + +gboolean core_option_log_level_parse(server *srv, plugin *p, size_t ndx, option *opt, gpointer *value) { + UNUSED(srv); + UNUSED(p); + UNUSED(ndx); + + assert(opt->type == OPTION_STRING); + + *value = (gpointer)log_level_from_string(opt->value.opt_string); + + TRACE(srv, "log.level assignment: %s", opt->value.opt_string->str); + + return TRUE; +} + +void core_option_log_level_free(server *srv, plugin *p, size_t ndx, gpointer value) { + UNUSED(srv); + UNUSED(p); + UNUSED(ndx); + UNUSED(value); +} + static const plugin_option options[] = { - { "debug.log_request_handling", OPTION_BOOLEAN, NULL, NULL}, - { "log.level", OPTION_STRING, NULL, NULL }, + { "debug.log_request_handling", OPTION_BOOLEAN, NULL, NULL }, + + { "log.target", OPTION_STRING, core_option_log_target_parse, core_option_log_target_free }, + { "log.level", OPTION_STRING, core_option_log_level_parse, core_option_log_level_free }, { "static-file.exclude", OPTION_LIST, NULL, NULL }, { NULL, 0, NULL, NULL } diff --git a/src/plugin_core.h b/src/plugin_core.h index 859c942..4283fe9 100644 --- a/src/plugin_core.h +++ b/src/plugin_core.h @@ -3,9 +3,11 @@ enum core_options_t { CORE_OPTION_DEBUG_REQUEST_HANDLING = 0, - CORE_OPTION_LOG_LEVEL = 1, - CORE_OPTION_STATIC_FILE_EXCLUDE = 2 + CORE_OPTION_LOG_TARGET = 1, + CORE_OPTION_LOG_LEVEL = 2, + + CORE_OPTION_STATIC_FILE_EXCLUDE = 3 }; /* the core plugin always has base index 0, as it is the first plugin loaded */