parent
6a9cea1404
commit
48bf0071e4
@ -0,0 +1,82 @@
|
||||
#ifndef _LIGHTTPD_ANGEL_LOG_H_
|
||||
#define _LIGHTTPD_ANGEL_LOG_H_
|
||||
|
||||
#ifndef _LIGHTTPD_ANGEL_BASE_H_
|
||||
#error Please include <lighttpd/angel_base.h> instead of this file
|
||||
#endif
|
||||
|
||||
/* #include <lighttpd/valgrind/valgrind.h> */
|
||||
|
||||
#define REMOVE_PATH_FROM_FILE 1
|
||||
#if REMOVE_PATH_FROM_FILE
|
||||
LI_API const char *remove_path(const char *path);
|
||||
#define REMOVE_PATH(file) remove_path(file)
|
||||
#else
|
||||
#define REMOVE_PATH(file) file
|
||||
#endif
|
||||
|
||||
#define SEGFAULT(srv, fmt, ...) \
|
||||
do { \
|
||||
log_write_(srv, LOG_LEVEL_ABORT, LOG_FLAG_TIMESTAMP, "(crashing) %s.%d: "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__); \
|
||||
/* VALGRIND_PRINTF_BACKTRACE(fmt, __VA_ARGS__); */\
|
||||
abort();\
|
||||
} while(0)
|
||||
|
||||
#define ERROR(srv, fmt, ...) \
|
||||
log_write(srv, LOG_LEVEL_ERROR, LOG_FLAG_TIMESTAMP, "error (%s:%d): "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
|
||||
|
||||
#define WARNING(srv, fmt, ...) \
|
||||
log_write(srv, LOG_LEVEL_WARNING, LOG_FLAG_TIMESTAMP, "warning (%s:%d): "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
|
||||
|
||||
#define INFO(srv, fmt, ...) \
|
||||
log_write(srv, LOG_LEVEL_INFO, LOG_FLAG_TIMESTAMP, "info (%s:%d): "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
|
||||
|
||||
#define DEBUG(srv, fmt, ...) \
|
||||
log_write(srv, LOG_LEVEL_DEBUG, LOG_FLAG_TIMESTAMP, "debug (%s:%d): "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
|
||||
|
||||
/* log messages from lighty always as ERROR */
|
||||
#define INSTANCE(srv, inst, msg) \
|
||||
log_write(srv, LOG_LEVEL_ERROR, LOG_FLAG_NONE, "lighttpd[%d]: %s", (int) inst->pid, msg)
|
||||
|
||||
typedef enum {
|
||||
LOG_LEVEL_DEBUG,
|
||||
LOG_LEVEL_INFO,
|
||||
LOG_LEVEL_WARNING,
|
||||
LOG_LEVEL_ERROR,
|
||||
LOG_LEVEL_ABORT
|
||||
} log_level_t;
|
||||
|
||||
#define LOG_LEVEL_COUNT (LOG_LEVEL_ABORT+1)
|
||||
|
||||
typedef enum {
|
||||
LOG_TYPE_STDERR,
|
||||
LOG_TYPE_FILE,
|
||||
LOG_TYPE_PIPE,
|
||||
LOG_TYPE_SYSLOG,
|
||||
LOG_TYPE_NONE
|
||||
} log_type_t;
|
||||
|
||||
#define LOG_FLAG_NONE (0x0) /* default flag */
|
||||
#define LOG_FLAG_TIMESTAMP (0x1) /* prepend a timestamp to the log message */
|
||||
|
||||
struct log_t;
|
||||
typedef struct log_t log_t;
|
||||
|
||||
struct log_t {
|
||||
log_type_t type;
|
||||
gboolean levels[LOG_LEVEL_COUNT];
|
||||
GString *path;
|
||||
gint fd;
|
||||
|
||||
time_t last_ts;
|
||||
GString *ts_cache;
|
||||
|
||||
GString *log_line;
|
||||
};
|
||||
|
||||
void log_init(server *srv);
|
||||
void log_clean(server *srv);
|
||||
|
||||
LI_API void log_write(server *srv, log_level_t log_level, guint flags, const gchar *fmt, ...) G_GNUC_PRINTF(4, 5);
|
||||
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
#ifndef _LIGHTTPD_ANGEL_PLUGIN_CORE_H_
|
||||
#define _LIGHTTPD_ANGEL_PLUGIN_CORE_H_
|
||||
|
||||
#include <lighttpd/angel_base.h>
|
||||
|
||||
gboolean plugin_core_init(server *srv);
|
||||
|
||||
#endif
|
@ -0,0 +1,66 @@
|
||||
|
||||
#include <lighttpd/angel_base.h>
|
||||
|
||||
#if REMOVE_PATH_FROM_FILE
|
||||
const char *remove_path(const char *path) {
|
||||
char *p = strrchr(path, DIR_SEPERATOR);
|
||||
if (NULL != p && *(p) != '\0') {
|
||||
return (p + 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
void log_init(server *srv) {
|
||||
srv->log.type = LOG_TYPE_STDERR;
|
||||
|
||||
srv->log.levels[LOG_LEVEL_ABORT] = TRUE;
|
||||
srv->log.levels[LOG_LEVEL_ERROR] = TRUE;
|
||||
srv->log.levels[LOG_LEVEL_WARNING] = TRUE;
|
||||
|
||||
srv->log.fd = -1;
|
||||
srv->log.ts_cache = g_string_sized_new(0);
|
||||
srv->log.log_line = g_string_sized_new(0);
|
||||
}
|
||||
|
||||
void log_clean(server *srv) {
|
||||
g_string_free(srv->log.ts_cache, TRUE);
|
||||
g_string_free(srv->log.log_line, TRUE);
|
||||
}
|
||||
|
||||
void log_write(server *srv, log_level_t log_level, guint flags, const gchar *fmt, ...) {
|
||||
va_list ap;
|
||||
GString *log_line = srv->log.log_line;
|
||||
|
||||
if (!srv->log.levels[log_level]) return;
|
||||
|
||||
g_string_truncate(log_line, 0);
|
||||
|
||||
/* for normal error messages, we prepend a timestamp */
|
||||
if (flags & LOG_FLAG_TIMESTAMP) {
|
||||
GString *log_ts = srv->log.ts_cache;
|
||||
time_t cur_ts;
|
||||
|
||||
cur_ts = (time_t)ev_now(srv->loop);
|
||||
|
||||
if (cur_ts != srv->log.last_ts) {
|
||||
gsize s;
|
||||
g_string_set_size(log_ts, 255);
|
||||
s = strftime(log_ts->str, log_ts->allocated_len, "%Y-%m-%d %H:%M:%S %Z: ", localtime(&cur_ts));
|
||||
|
||||
g_string_set_size(log_ts, s);
|
||||
|
||||
srv->log.last_ts = cur_ts;
|
||||
}
|
||||
|
||||
g_string_append_len(log_line, GSTR_LEN(log_ts));
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
g_string_append_vprintf(log_line, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
g_string_append_len(log_line, CONST_STR_LEN("\n"));
|
||||
|
||||
fprintf(stderr, "%s", log_line->str);
|
||||
}
|
@ -0,0 +1,351 @@
|
||||
|
||||
#include <lighttpd/angel_base.h>
|
||||
#include <lighttpd/angel_config_parser.h>
|
||||
#include <lighttpd/angel_plugin_core.h>
|
||||
|
||||
/* internal structures */
|
||||
|
||||
typedef struct server_item server_item;
|
||||
struct server_item {
|
||||
plugin *p;
|
||||
guint option_count;
|
||||
const plugin_item *p_item;
|
||||
};
|
||||
|
||||
typedef struct server_module server_module;
|
||||
struct server_module {
|
||||
guint refcount;
|
||||
gchar *name;
|
||||
server *srv;
|
||||
module *mod;
|
||||
GPtrArray *plugins; /* plugin* */
|
||||
};
|
||||
|
||||
static void _server_item_free(gpointer p) {
|
||||
g_slice_free(server_item, p);
|
||||
}
|
||||
|
||||
static server_item* server_item_new(plugin *p, const plugin_item *p_item) {
|
||||
server_item *si = g_slice_new(server_item);
|
||||
const plugin_item_option *pio;
|
||||
guint cnt;
|
||||
for (pio = p_item->options, cnt = 0; pio->name; pio++, cnt++) ;
|
||||
si->p = p;
|
||||
si->option_count = cnt;
|
||||
si->p_item = p_item;
|
||||
return si;
|
||||
}
|
||||
|
||||
static void plugin_free(server *srv, plugin *p) {
|
||||
if (p->handle_free) p->handle_free(srv, p);
|
||||
g_slice_free(plugin, p);
|
||||
}
|
||||
|
||||
static plugin* plugin_new(const char *name) {
|
||||
plugin *p = g_slice_new0(plugin);
|
||||
p->name = name;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void _server_module_release(gpointer d) {
|
||||
server_module *sm = d;
|
||||
guint i;
|
||||
|
||||
g_assert(sm->refcount > 0);
|
||||
if (0 != --sm->refcount) return;
|
||||
|
||||
for (i = sm->plugins->len; i-- > 0; ) {
|
||||
plugin *p = g_ptr_array_index(sm->plugins, i);
|
||||
plugin_free(sm->srv, p);
|
||||
}
|
||||
g_ptr_array_free(sm->plugins, TRUE);
|
||||
if (sm->mod) module_release(sm->srv->plugins.modules, sm->mod);
|
||||
g_free(sm->name);
|
||||
g_slice_free(server_module, sm);
|
||||
}
|
||||
|
||||
static void server_module_acquire(server_module *sm) {
|
||||
g_assert(sm->refcount > 0);
|
||||
sm->refcount++;
|
||||
}
|
||||
|
||||
static server_module* server_module_new(server *srv, const gchar *name) { /* module is set later */
|
||||
server_module *sm = g_slice_new0(server_module);
|
||||
sm->refcount = 1;
|
||||
sm->srv = srv;
|
||||
sm->plugins = g_ptr_array_new();
|
||||
sm->name = g_strdup(name);
|
||||
return sm;
|
||||
}
|
||||
|
||||
void plugins_init(server *srv, const gchar *module_dir) {
|
||||
Plugins *ps = &srv->plugins;
|
||||
|
||||
ps->modules = modules_new(srv, module_dir);
|
||||
|
||||
ps->items = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, _server_item_free);
|
||||
ps->load_items = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, _server_item_free);
|
||||
|
||||
ps->module_refs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, _server_module_release);
|
||||
ps->load_module_refs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, _server_module_release);
|
||||
|
||||
ps->plugins = g_ptr_array_new();
|
||||
ps->load_plugins = g_ptr_array_new();
|
||||
}
|
||||
|
||||
void plugins_clear(server *srv) {
|
||||
Plugins *ps = &srv->plugins;
|
||||
|
||||
plugins_config_clean(srv);
|
||||
|
||||
g_hash_table_destroy(ps->items);
|
||||
g_hash_table_destroy(ps->load_items);
|
||||
|
||||
g_hash_table_destroy(ps->module_refs);
|
||||
g_hash_table_destroy(ps->load_module_refs);
|
||||
|
||||
g_ptr_array_free(ps->plugins, TRUE);
|
||||
g_ptr_array_free(ps->load_plugins, TRUE);
|
||||
|
||||
if (ps->config_filename) g_string_free(ps->config_filename, TRUE);
|
||||
|
||||
modules_free(ps->modules);
|
||||
}
|
||||
|
||||
void plugins_config_clean(server *srv) {
|
||||
Plugins *ps = &srv->plugins;
|
||||
guint i;
|
||||
|
||||
for (i = ps->load_plugins->len; i-- > 0; ) {
|
||||
plugin *p = g_ptr_array_index(ps->load_plugins, i);
|
||||
if (p->handle_clean_config) p->handle_clean_config(srv, p);
|
||||
}
|
||||
|
||||
g_hash_table_remove_all(ps->load_items);
|
||||
g_hash_table_remove_all(ps->load_module_refs);
|
||||
g_ptr_array_set_size(ps->load_plugins, 0);
|
||||
}
|
||||
|
||||
gboolean plugins_config_load(server *srv, const gchar *filename) {
|
||||
Plugins *ps = &srv->plugins;
|
||||
GError *error = NULL;
|
||||
guint i;
|
||||
|
||||
if (!angel_config_parse_file(srv, filename, &error)) {
|
||||
ERROR(srv, "failed to parse config file: %s\n", error->message);
|
||||
g_error_free(error);
|
||||
plugins_config_clean(srv);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check new config */
|
||||
for (i = ps->plugins->len; i-- > 0; ) {
|
||||
plugin *p = g_ptr_array_index(ps->load_plugins, i);
|
||||
if (p->handle_check_config) {
|
||||
if (!p->handle_check_config(srv, p)) {
|
||||
plugins_config_clean(srv);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* activate new config */
|
||||
for (i = ps->plugins->len; i-- > 0; ) {
|
||||
plugin *p = g_ptr_array_index(ps->load_plugins, i);
|
||||
if (p->handle_activate_config) {
|
||||
p->handle_activate_config(srv, p);
|
||||
}
|
||||
}
|
||||
|
||||
{ /* swap the arrays */
|
||||
GPtrArray *tmp = ps->load_plugins; ps->load_plugins = ps->plugins; ps->plugins = tmp;
|
||||
}
|
||||
{ /* swap the hash tables */
|
||||
GHashTable *tmp;
|
||||
tmp = ps->load_items; ps->load_items = ps->items; ps->items = tmp;
|
||||
tmp = ps->load_module_refs; ps->load_module_refs = ps->module_refs; ps->module_refs = tmp;
|
||||
}
|
||||
g_hash_table_remove_all(ps->load_items);
|
||||
g_hash_table_remove_all(ps->load_module_refs);
|
||||
g_ptr_array_set_size(ps->load_plugins, 0);
|
||||
|
||||
if (!ps->config_filename) {
|
||||
ps->config_filename = g_string_new(filename);
|
||||
} else {
|
||||
g_string_assign(ps->config_filename, filename);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean plugins_handle_item(server *srv, GString *itemname, value *hash) {
|
||||
Plugins *ps = &srv->plugins;
|
||||
server_item *si;
|
||||
|
||||
#if 1
|
||||
/* debug items */
|
||||
{
|
||||
GString *tmp = value_to_string(hash);
|
||||
ERROR(srv, "Item '%s': %s\n", itemname->str, tmp->str);
|
||||
g_string_free(tmp, TRUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
si = g_hash_table_lookup(ps->load_items, itemname->str);
|
||||
if (!si) {
|
||||
WARNING(srv, "Unknown item '%s' - perhaps you forgot to load the module? (ignored)", itemname->str);
|
||||
} else {
|
||||
value **optlist = g_slice_alloc0(sizeof(value*) * si->option_count);
|
||||
GHashTableIter opti;
|
||||
gpointer k, v;
|
||||
guint i;
|
||||
gboolean valid = TRUE;
|
||||
|
||||
/* find options and assign them by id */
|
||||
g_hash_table_iter_init(&opti, hash->data.hash);
|
||||
while (g_hash_table_iter_next(&opti, &k, &v)) {
|
||||
const gchar *optkey = ((GString*) k)->str;
|
||||
for (i = 0; i < si->option_count; i++) {
|
||||
if (0 == g_strcmp0(si->p_item->options[i].name, optkey)) break;
|
||||
}
|
||||
if (i == si->option_count) {
|
||||
WARNING(srv, "Unknown option '%s' in item '%s' (ignored)", optkey, itemname->str);
|
||||
} else {
|
||||
optlist[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
/* validate options */
|
||||
for (i = 0; i < si->option_count; i++) {
|
||||
const plugin_item_option *pi = &si->p_item->options[i];
|
||||
if (0 != (pi->flags & PLUGIN_ITEM_OPTION_MANDATORY)) {
|
||||
if (!optlist[i]) {
|
||||
ERROR(srv, "Missing mandatory option '%s' in item '%s'", pi->name, itemname->str);
|
||||
valid = FALSE;
|
||||
}
|
||||
}
|
||||
if (pi->type != VALUE_NONE && optlist[i] && optlist[i]->type != pi->type) {
|
||||
/* TODO: convert from string if possible */
|
||||
ERROR(srv, "Invalid value type of option '%s' in item '%s', got '%s' but expected '%s'",
|
||||
pi->name, itemname->str, value_type_string(optlist[i]->type), value_type_string(pi->type));
|
||||
valid = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
g_assert(si->p_item->handle_parse_item);
|
||||
si->p_item->handle_parse_item(srv, si->p, optlist);
|
||||
}
|
||||
|
||||
g_slice_free1(sizeof(value*) * si->option_count, optlist);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean plugins_activate_module(server *srv, server_module *sm) {
|
||||
Plugins *ps = &srv->plugins;
|
||||
plugin *p;
|
||||
const plugin_item *pi;
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < sm->plugins->len; i++) {
|
||||
p = g_ptr_array_index(sm->plugins, i);
|
||||
g_ptr_array_add(ps->load_plugins, p);
|
||||
if (!p->items) continue;
|
||||
|
||||
for (pi = p->items; pi->name; pi++) {
|
||||
server_item *si;
|
||||
if (NULL != (si = g_hash_table_lookup(ps->load_items, pi->name))) {
|
||||
ERROR(srv, "Plugin item name conflict: cannot load '%s' for plugin '%s' (already provided by plugin '%s')",
|
||||
pi->name, p->name, si->p->name);
|
||||
goto item_collission;
|
||||
} else {
|
||||
si = server_item_new(p, pi);
|
||||
g_hash_table_insert(ps->load_items, (gpointer) pi->name, si);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
item_collission:
|
||||
/* removed added items and plugins */
|
||||
for ( ; pi-- != p->items; ) {
|
||||
g_hash_table_remove(ps->load_items, pi->name);
|
||||
}
|
||||
|
||||
g_ptr_array_set_size(ps->load_plugins, ps->load_plugins->len - i+1);
|
||||
|
||||
for ( ; i-- > 0; ) {
|
||||
p = g_ptr_array_index(sm->plugins, i);
|
||||
if (!p->items) continue;
|
||||
|
||||
for (pi = p->items; pi->name; pi++) {
|
||||
g_hash_table_remove(ps->load_items, pi->name);
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean plugins_load_module(server *srv, const gchar *name) {
|
||||
Plugins *ps = &srv->plugins;
|
||||
server_module *sm;
|
||||
const gchar* modname = name ? name : "core";
|
||||
|
||||
sm = g_hash_table_lookup(ps->load_module_refs, modname);
|
||||
if (sm) return TRUE; /* already loaded */
|
||||
sm = g_hash_table_lookup(ps->module_refs, modname);
|
||||
if (sm) { /* loaded in previous config */
|
||||
server_module_acquire(sm);
|
||||
g_hash_table_insert(ps->load_module_refs, sm->name, sm);
|
||||
} else { /* not loaded yet */
|
||||
module *mod;
|
||||
sm = server_module_new(srv, modname);
|
||||
g_hash_table_insert(ps->load_module_refs, sm->name, sm);
|
||||
if (name) {
|
||||
mod = module_load(ps->modules, name);
|
||||
|
||||
if (!mod) {
|
||||
_server_module_release(sm);
|
||||
return FALSE;
|
||||
}
|
||||
sm->mod = mod;
|
||||
} else {
|
||||
if (!plugin_core_init(srv)) {
|
||||
_server_module_release(sm);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!plugins_activate_module(srv, sm)) {
|
||||
_server_module_release(sm);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
plugin *angel_plugin_register(server *srv, module *mod, const gchar *name, PluginInit init) {
|
||||
Plugins *ps = &srv->plugins;
|
||||
server_module *sm;
|
||||
plugin *p;
|
||||
const gchar* modname = mod ? mod->name->str : "core";
|
||||
|
||||
sm = g_hash_table_lookup(ps->load_module_refs, modname);
|
||||
if (!sm) {
|
||||
ERROR(srv, "Module '%s' not loaded; cannot load plugin '%s'", mod->name->str, name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = plugin_new(name);
|
||||
if (!init(srv, p)) {
|
||||
plugin_free(srv, p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_ptr_array_add(sm->plugins, p);
|
||||
|
||||
return p;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
|
||||
#include <lighttpd/angel_plugin_core.h>
|
||||
|
||||
gboolean plugin_core_init(server *srv) {
|
||||
/* load core plugins */
|
||||
return TRUE;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
|
||||
#include <lighttpd/angel_base.h>
|
||||
|
||||
server* server_new(const gchar *module_dir) {
|
||||
server *srv = g_slice_new0(server);
|
||||
|
||||
/* TODO: handle sinals */
|
||||
|
||||
srv->loop = ev_default_loop(0);
|
||||
log_init(srv);
|
||||
plugins_init(srv, module_dir);
|
||||
return srv;
|
||||
}
|
||||
|
||||
void server_free(server* srv) {
|
||||
plugins_clear(srv);
|
||||
|
||||
log_clean(srv);
|
||||
g_slice_free(server, srv);
|
||||
}
|
Loading…
Reference in new issue