[log] Add basic api to open log files via angel

personal/stbuehler/wip
Stefan Bühler 13 years ago
parent 97088f667e
commit c35cf21c52

@ -3,6 +3,8 @@
typedef void (*liAngelListenCB)(liServer *srv, int fd, gpointer data);
typedef void (*liAngelLogOpen)(liServer *srv, int fd, gpointer data);
/* interface to the angel; implementation needs to work without angel too */
LI_API void li_angel_setup(liServer *srv);
@ -12,9 +14,11 @@ LI_API void li_angel_listen(liServer *srv, GString *str, liAngelListenCB cb, gpo
/* send log messages during startup to angel, frees the string */
LI_API void li_angel_log(liServer *srv, GString *str);
LI_API void li_angel_log_open_file(liServer *srv, GString *filename, liAngelLogOpen, gpointer data);
/* angle_fake definitions, only for internal use */
int li_angel_fake_listen(liServer *srv, GString *str);
gboolean li_angel_fake_log(liServer *srv, GString *str);
int li_angel_fake_log_open_file(liServer *srv, GString *filename);
#endif

@ -90,8 +90,11 @@ struct liLogEntry {
GList queue_link;
};
/* determines the type of a log target by the path given. /absolute/path = file; |app = pipe; stderr = stderr; syslog = syslog */
LI_API liLogType li_log_type_from_path(GString *path);
/* determines the type of a log target by the path given. /absolute/path = file; |app = pipe; stderr = stderr; syslog = syslog;
* returns the begin of the parameter string in *param if param != NULL (filename for /absolute/path or file:///absolute/path)
* *param is either NULL or points into the path string!
*/
LI_API liLogType li_log_type_from_path(GString *path, gchar **param);
LI_API liLogLevel li_log_level_from_string(GString *str);
LI_API gchar* li_log_level_str(liLogLevel log_level);

@ -2,6 +2,12 @@
#include <lighttpd/angel_plugin_core.h>
#include <lighttpd/ip_parsers.h>
#include <fnmatch.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
typedef struct listen_socket listen_socket;
typedef struct listen_ref_resource listen_ref_resource;
@ -9,8 +15,6 @@ typedef struct listen_ref_resource listen_ref_resource;
# define DEFAULT_LIBEXECDIR "/usr/local/lib/lighttpd2"
#endif
#include <fnmatch.h>
struct listen_socket {
gint refcount;
@ -24,9 +28,6 @@ struct listen_ref_resource {
listen_socket *sock;
};
#include <pwd.h>
#include <grp.h>
static void core_instance_parse(liServer *srv, liPlugin *p, liValue **options) {
GPtrArray *cmd, *env;
gchar **cmdarr, **envarr;
@ -614,6 +615,59 @@ static void core_reached_state(liServer *srv, liPlugin *p, liInstance *i, gint32
}
}
static void core_log_open_file(liServer *srv, liPlugin *p, liInstance *i, gint32 id, GString *data) {
GError *err = NULL;
int fd = -1;
GArray *fds;
UNUSED(p);
DEBUG(srv, "core_log_open_file(%i) '%s'", id, data->str);
if (-1 == id) return; /* ignore simple calls */
li_path_simplify(data);
/* TODO: make path configurable */
if (g_str_has_prefix(data->str, "/var/log/lighttpd2/")) {
/* files can be read by everyone. if you don't like that, restrict access on the directory */
/* if you need group write access for a specific group, use chmod g+s on the directory */
/* "maybe-todo": add options for mode/owner/group */
fd = open(data->str, O_RDWR | O_CREAT | O_APPEND, 0664);
if (-1 == fd) {
int e = errno;
GString *error = g_string_sized_new(0);
g_string_printf(error, "Couldn't open log file '%s': '%s'", data->str, g_strerror(e));
ERROR(srv, "Couldn't open log file '%s': %s", data->str, g_strerror(e));
if (!li_angel_send_result(i->acon, id, error, NULL, NULL, &err)) {
ERROR(srv, "Couldn't send result: %s", err->message);
g_error_free(err);
}
return;
}
} else {
GString *error = g_string_sized_new(0);
g_string_printf(error, "Couldn't open log file '%s': path not allowed", data->str);
if (!li_angel_send_result(i->acon, id, error, NULL, NULL, &err)) {
ERROR(srv, "Couldn't send result: %s", err->message);
g_error_free(err);
}
return;
}
fds = g_array_new(FALSE, FALSE, sizeof(int));
g_array_append_val(fds, fd);
if (!li_angel_send_result(i->acon, id, NULL, NULL, fds, &err)) {
ERROR(srv, "Couldn't send result: %s", err->message);
g_error_free(err);
return;
}
}
static void core_clean(liServer *srv, liPlugin *p);
static void core_free(liServer *srv, liPlugin *p) {
liPluginCoreConfig *config = (liPluginCoreConfig*) p->data;
@ -748,6 +802,7 @@ static gboolean core_init(liServer *srv, liPlugin *p) {
li_angel_plugin_add_angel_cb(p, "listen", core_listen);
li_angel_plugin_add_angel_cb(p, "reached-state", core_reached_state);
li_angel_plugin_add_angel_cb(p, "log-open-file", core_log_open_file);
ev_signal_init(&config->sig_hup, core_handle_sig_hup, SIGHUP);
config->sig_hup.data = config;

@ -193,7 +193,7 @@ liProc* li_proc_new(liServer *srv, gchar **args, gchar **env, uid_t uid, gid_t g
execve(args[0], args, env);
g_printerr("exec('%s') failed: %s\n", args[0], g_strerror(errno));
exit(-1);
abort();
break;
case -1:

@ -32,7 +32,7 @@ static void angel_close_cb(liAngelConnection *acon, GError *err) {
liServer *srv = acon->data;
ERROR(srv, "li_fatal: angel connection close: %s", err ? err->message : g_strerror(errno));
if (err) g_error_free(err);
exit(1);
abort();
}
void li_angel_setup(liServer *srv) {
@ -117,3 +117,68 @@ void li_angel_listen(liServer *srv, GString *str, liAngelListenCB cb, gpointer d
void li_angel_log(liServer *srv, GString *str) {
li_angel_fake_log(srv, str);
}
typedef struct angel_log_cb_ctx angel_log_cb_ctx;
struct angel_log_cb_ctx {
liServer *srv;
liAngelLogOpen cb;
gpointer data;
GString *logname;
};
static void li_angel_log_open_cb(liAngelCall *acall, gpointer pctx, gboolean timeout, GString *error, GString *data, GArray *fds) {
angel_log_cb_ctx ctx = * (angel_log_cb_ctx*) pctx;
liServer *srv = ctx.srv;
UNUSED(data);
li_angel_call_free(acall);
g_slice_free(angel_log_cb_ctx, pctx);
if (timeout) {
ERROR(srv, "Couldn't open log file '%s': timeout", ctx.logname->str);
goto failed;
}
if (error->len > 0) {
ERROR(srv, "Couldn't open log file '%s': %s", ctx.logname->str, error->str);
goto failed;
}
if (NULL == fds || fds->len != 1) {
ERROR(srv, "Couldn't open log file '%s': no or too many filedescriptors (%i)", ctx.logname->str, (int) (NULL == fds ? 0 : fds->len));
goto failed;
}
ctx.cb(srv, g_array_index(fds, int, 0), ctx.data);
g_array_set_size(fds, 0);
goto cleanup;
failed:
ctx.cb(srv, -1, ctx.data);
cleanup:
g_string_free(ctx.logname, TRUE);
}
void li_angel_log_open_file(liServer *srv, GString *filename, liAngelLogOpen cb, gpointer data) {
if (srv->acon) {
liAngelCall *acall = li_angel_call_new(li_angel_log_open_cb, 10.0);
angel_log_cb_ctx *ctx = g_slice_new0(angel_log_cb_ctx);
GError *err = NULL;
ctx->srv = srv;
ctx->cb = cb;
ctx->data = data;
ctx->logname = g_string_new_len(GSTR_LEN(filename));
acall->context = ctx;
if (!li_angel_send_call(srv->acon, CONST_STR_LEN("core"), CONST_STR_LEN("log-open-file"), acall, g_string_new_len(GSTR_LEN(filename)), &err)) {
ERROR(srv, "couldn't send call: %s", err->message);
g_error_free(err);
}
} else {
int fd = li_angel_fake_log_open_file(srv, filename);
cb(srv, fd, data);
}
}

@ -3,6 +3,8 @@
#include <lighttpd/angel.h>
#include <lighttpd/ip_parsers.h>
#include <fcntl.h>
/* listen to a socket */
int li_angel_fake_listen(liServer *srv, GString *str) {
guint32 ipv4;
@ -164,3 +166,14 @@ gboolean li_angel_fake_log(liServer *srv, GString *str) {
g_string_free(str, TRUE);
return TRUE;
}
int li_angel_fake_log_open_file(liServer *srv, GString *filename) {
int fd;
fd = open(filename->str, O_RDWR | O_CREAT | O_APPEND, 0660);
if (-1 == fd) {
ERROR(srv, "failed to open log file '%s': %s", filename->str, g_strerror(errno));
}
return fd;
}

@ -8,8 +8,7 @@
#include <lighttpd/base.h>
#include <lighttpd/plugin_core.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#define LOG_DEFAULT_TS_FORMAT "%d/%b/%Y %T %Z"
@ -47,35 +46,32 @@ static liLog *log_open(liServer *srv, GString *path) {
else
log = NULL;
if (!log) {
if (NULL == log) {
/* log not open */
gint fd = -1;
liLogType type = li_log_type_from_path(path);
gchar *param = NULL;
liLogType type = li_log_type_from_path(path, &param);
GString sparam = { param, (param != NULL ? path->len - (param - path->str) : 0), 0 };
switch (type) {
case LI_LOG_TYPE_STDERR:
fd = STDERR_FILENO;
break;
case LI_LOG_TYPE_FILE:
/* todo: open via angel */
fd = open(path->str, O_RDWR | O_CREAT | O_APPEND, 0660);
if (fd == -1) {
int err = errno;
GString *str = g_string_sized_new(255);
g_string_append_printf(str, "(error) %s.%d: failed to open log file '%s': %s", LI_REMOVE_PATH(__FILE__), __LINE__, path->str, g_strerror(err));
/* li_log_write_stderr(srv, str->str, TRUE); */
g_string_free(str, TRUE);
return NULL;
}
/* TODO: open via angel */
fd = li_angel_fake_log_open_file(srv, &sparam);
break;
case LI_LOG_TYPE_PIPE:
ERROR(srv, "%s", "pipe logging not supported yet");
break;
case LI_LOG_TYPE_SYSLOG:
/* todo */
assert(NULL);
ERROR(srv, "%s", "syslog not supported yet");
break;
case LI_LOG_TYPE_NONE:
return NULL;
}
/* Even if -1 == fd we create an entry, so we don't throw an error every time */
log = g_slice_new0(liLog);
log->type = type;
log->path = g_string_new_len(GSTR_LEN(path));
@ -95,7 +91,7 @@ static void log_close(liServer *srv, liLog *log) {
li_waitqueue_remove(&srv->logs.close_queue, &log->wqelem);
if (log->type == LI_LOG_TYPE_FILE || log->type == LI_LOG_TYPE_PIPE) {
close(log->fd);
if (-1 != log->fd) close(log->fd);
}
/*g_print("log_close(\"%s\")\n", log->path->str);*/
@ -297,12 +293,7 @@ static GString *log_timestamp_format(liServer *srv, liLogTimestamp *ts) {
static void log_watcher_cb(struct ev_loop *loop, ev_async *w, int revents) {
liServer *srv = (liServer*) w->data;
liLog *log;
liLogEntry *log_entry;
GList *queue_link, *queue_link_next;
GString *msg;
gssize bytes_written;
gssize write_res;
UNUSED(loop);
UNUSED(revents);
@ -325,15 +316,11 @@ static void log_watcher_cb(struct ev_loop *loop, ev_async *w, int revents) {
g_static_mutex_unlock(&srv->logs.write_queue_mutex);
while (queue_link) {
log_entry = queue_link->data;
log = log_open(srv, log_entry->path);
msg = log_entry->msg;
bytes_written = 0;
if (!log) {
li_log_write_stderr(srv, log_entry->msg->str, TRUE);
goto next;
}
liLog *log;
liLogEntry *log_entry = queue_link->data;
GString *msg = log_entry->msg;
gssize bytes_written = 0;
gssize write_res;
if (log_entry->flags & LOG_FLAG_TIMESTAMP) {
log_timestamp_format(srv, log_entry->ts);
@ -343,6 +330,13 @@ static void log_watcher_cb(struct ev_loop *loop, ev_async *w, int revents) {
g_string_append_len(msg, CONST_STR_LEN("\n"));
log = log_open(srv, log_entry->path);
if (NULL == log || -1 == log->fd) {
li_log_write_stderr(srv, msg->str, TRUE);
goto next;
}
/* todo: support for other logtargets than files */
while (bytes_written < (gssize)msg->len) {
write_res = write(log->fd, msg->str + bytes_written, msg->len - bytes_written);
@ -371,7 +365,7 @@ static void log_watcher_cb(struct ev_loop *loop, ev_async *w, int revents) {
}
}
next:
next:
queue_link_next = queue_link->next;
g_string_free(log_entry->path, TRUE);
g_string_free(log_entry->msg, TRUE);
@ -393,37 +387,42 @@ static void log_watcher_cb(struct ev_loop *loop, ev_async *w, int revents) {
return;
}
liLogType li_log_type_from_path(GString *path) {
#define RET(type, offset) do { if (NULL != param) *param = path->str + offset; return type; } while(0)
#define RET_PAR(type, par) do { if (NULL != param) *param = par; return type; } while(0)
#define TRY_SCHEME(scheme, type) do { if (g_str_has_prefix(path->str, scheme)) RET(type, sizeof(scheme)-1); } while(0)
liLogType li_log_type_from_path(GString *path, gchar **param) {
if (path->len == 0)
return LI_LOG_TYPE_NONE;
/* look for scheme:// paths */
if (g_str_has_prefix(path->str, "file://"))
return LI_LOG_TYPE_FILE;
if (g_str_has_prefix(path->str, "pipe://"))
return LI_LOG_TYPE_PIPE;
if (g_str_has_prefix(path->str, "stderr://"))
return LI_LOG_TYPE_STDERR;
if (g_str_has_prefix(path->str, "syslog://"))
return LI_LOG_TYPE_SYSLOG;
/* look for scheme: paths */
TRY_SCHEME("file:", LI_LOG_TYPE_FILE);
TRY_SCHEME("pipe:", LI_LOG_TYPE_PIPE);
TRY_SCHEME("stderr:", LI_LOG_TYPE_STDERR);
TRY_SCHEME("syslog:", LI_LOG_TYPE_SYSLOG);
/* targets starting with a slash are absolute paths and therefor file targets */
if (*path->str == '/')
return LI_LOG_TYPE_FILE;
RET(LI_LOG_TYPE_FILE, 0);
/* targets starting with a pipe are ... pipes! */
if (*path->str == '|')
return LI_LOG_TYPE_PIPE;
if (*path->str == '|') {
guint i = 1;
while (path->str[i] == ' ') i++; /* skip spaces */
RET(LI_LOG_TYPE_PIPE, i);
}
if (g_str_equal(path->str, "stderr"))
return LI_LOG_TYPE_STDERR;
RET_PAR(LI_LOG_TYPE_STDERR, NULL);
if (g_str_equal(path->str, "syslog"))
return LI_LOG_TYPE_SYSLOG;
RET_PAR(LI_LOG_TYPE_SYSLOG, NULL);
/* fall back to stderr */
return LI_LOG_TYPE_STDERR;
RET_PAR(LI_LOG_TYPE_STDERR, NULL);
}
#undef RET
#undef RET_PAR
#undef TRY_SCHEME
liLogLevel li_log_level_from_string(GString *str) {
if (g_str_equal(str->str, "debug"))

@ -216,7 +216,7 @@ static void profiler_write(gchar *str, gint len) {
if (-1 == res) {
fputs("error writing to profiler output file\n", stderr);
fflush(stderr);
exit(1);
abort();
}
written += res;
@ -237,7 +237,7 @@ void li_profiler_enable(gchar *output_path) {
if (-1 == profiler_output_fd) {
fputs("error opening profiler output file\n", stderr);
fflush(stderr);
exit(1);
abort();
}
}

@ -79,7 +79,7 @@ static void sigint_cb(struct ev_loop *loop, struct ev_signal *w, int revents) {
li_server_goto_state(srv, LI_SERVER_DOWN);
} else {
INFO(srv, "%s", "Got second signal, force shutdown");
exit(1);
abort();
}
}

Loading…
Cancel
Save