diff --git a/include/lighttpd/angel.h b/include/lighttpd/angel.h index 0bd995f3..2a2ec006 100644 --- a/include/lighttpd/angel.h +++ b/include/lighttpd/angel.h @@ -15,6 +15,7 @@ LI_API void li_angel_listen(liServer *srv, GString *str, liAngelListenCB cb, gpo 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); +LI_API void li_angel_log_open_pipe(liServer *srv, GString *filename, liAngelLogOpen, gpointer data); /* angle_fake definitions, only for internal use */ int li_angel_fake_listen(liServer *srv, GString *str); diff --git a/include/lighttpd/angel_plugin_core.h b/include/lighttpd/angel_plugin_core.h index 9a6d119b..53a41189 100644 --- a/include/lighttpd/angel_plugin_core.h +++ b/include/lighttpd/angel_plugin_core.h @@ -9,6 +9,7 @@ struct liPluginCoreConfig { gboolean load_failed; liInstanceConf *load_instconf; GPtrArray *load_listen_masks; + GHashTable *load_pipes; /* Running */ liInstanceConf *instconf; diff --git a/src/angel/angel_plugin_core.c b/src/angel/angel_plugin_core.c index fdef5c6a..4779518d 100644 --- a/src/angel/angel_plugin_core.c +++ b/src/angel/angel_plugin_core.c @@ -36,6 +36,7 @@ static void core_instance_parse(liServer *srv, liPlugin *p, liValue **options) { gid_t gid = -1; GString *user = NULL; gint64 rlim_core = -1, rlim_nofile = -1; + liValue *lv = NULL; if (config->load_instconf) { ERROR(srv, "%s", "Already configure the instance"); @@ -185,6 +186,13 @@ static void core_instance_parse(liServer *srv, liPlugin *p, liValue **options) { if (options[9]) rlim_core = options[9]->data.number; if (options[10]) rlim_nofile = options[10]->data.number; + if (options[11]) { + lv = li_value_copy(options[11]); + config->load_pipes = lv->data.hash; + lv->type = LI_VALUE_NONE; + li_value_free(lv); + } + g_ptr_array_add(cmd, NULL); g_ptr_array_add(env, NULL); cmdarr = (gchar**) g_ptr_array_free(cmd, FALSE); @@ -204,6 +212,7 @@ static const liPluginItemOption core_instance_options[] = { /* 8 */ { "copy-env", LI_VALUE_LIST, 0 }, /* 9 */ { "max-core-file-size", LI_VALUE_NUMBER, 0 }, /* 10 */ { "max-open-files", LI_VALUE_NUMBER, 0 }, + /* 11 */ { "pipes", LI_VALUE_HASH, 0 }, { NULL, 0, 0 } }; @@ -668,6 +677,75 @@ static void core_log_open_file(liServer *srv, liPlugin *p, liInstance *i, gint32 } } +static void core_log_open_pipe(liServer *srv, liPlugin *p, liInstance *i, gint32 id, GString *data) { + liPluginCoreConfig *config = (liPluginCoreConfig*) p->data; + liValue *pipeval = NULL; + + GError *err = NULL; + int fd = -1; + GArray *fds; + + int to_log_fds[2]; + pid_t pid; + + DEBUG(srv, "core_log_open_pipe(%i, '%s')", id, data->str); + + if (-1 == id) return; /* ignore simple calls */ + + pipeval = g_hash_table_lookup(config->load_pipes, data); + if (pipeval) { + if (!pipe(to_log_fds)) { + switch (pid = fork()) { + case 0: + close(STDIN_FILENO); + dup2(to_log_fds[0], STDIN_FILENO); + close(to_log_fds[0]); + close(to_log_fds[1]); + execl("/bin/sh", "sh", "-c", pipeval->data.string->str, (char*)NULL); + ERROR(srv, "core_log_open_pipe(%i, '%s'): execl failed (%s)", id, data->str, strerror(errno)); + exit(-1); + break; + case -1: + ERROR(srv, "core_log_open_pipe(%i, '%s'): fork failed (%s)", id, data->str, strerror(errno)); + break; + default: + close(to_log_fds[0]); + fd = to_log_fds[1]; + break; + } + } + 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 pipe '%s': unconfigured pipe", 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; @@ -693,6 +771,8 @@ static void core_free(liServer *srv, liPlugin *p) { } g_ptr_array_free(config->listen_masks, TRUE); g_ptr_array_free(config->load_listen_masks, TRUE); + if (config->load_pipes != NULL) + g_hash_table_destroy(config->load_pipes); g_hash_table_destroy(config->listen_sockets); config->listen_masks = NULL; config->load_listen_masks = NULL; @@ -798,11 +878,13 @@ static gboolean core_init(liServer *srv, liPlugin *p) { config->listen_masks = g_ptr_array_new(); config->load_listen_masks = g_ptr_array_new(); + config->load_pipes = NULL; config->listen_sockets = g_hash_table_new_full(li_hash_sockaddr, li_equal_sockaddr, NULL, _listen_socket_free); 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); + li_angel_plugin_add_angel_cb(p, "log-open-pipe", core_log_open_pipe); ev_signal_init(&config->sig_hup, core_handle_sig_hup, SIGHUP); config->sig_hup.data = config; diff --git a/src/main/angel.c b/src/main/angel.c index 05a4d299..e27f7409 100644 --- a/src/main/angel.c +++ b/src/main/angel.c @@ -182,3 +182,24 @@ void li_angel_log_open_file(liServer *srv, GString *filename, liAngelLogOpen cb, cb(srv, fd, data); } } + +void li_angel_log_open_pipe(liServer *srv, GString *pipename, 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(pipename)); + + acall->context = ctx; + if (!li_angel_send_call(srv->acon, CONST_STR_LEN("core"), CONST_STR_LEN("log-open-pipe"), acall, g_string_new_len(GSTR_LEN(pipename)), &err)) { + ERROR(srv, "couldn't send call: %s", err->message); + g_error_free(err); + } + } else { + ERROR(srv, "angel required for: %s", "log-open-pipe"); + } +} diff --git a/src/main/log.c b/src/main/log.c index 48a3d3ab..a20bcf19 100644 --- a/src/main/log.c +++ b/src/main/log.c @@ -38,6 +38,16 @@ static void li_log_write_stderr(liServer *srv, const gchar *msg, gboolean newlin g_static_mutex_unlock(&mtx); } +static void log_open_pipe_cb(liServer *srv, int fd, gpointer data) { + liLogTarget *log; + log = data; + + if (log == NULL) + ERROR(srv, "log_open_pipe_cb(%d, %s): no log found", fd, log->path->str); + else if (log->fd < 0) + log->fd = fd; +} + static liLogTarget *log_open(liServer *srv, GString *path) { liLogTarget *log; @@ -50,6 +60,14 @@ static liLogTarget *log_open(liServer *srv, GString *path) { liLogType type = li_log_type_from_path(path, ¶m); GString sparam = { param, (param != NULL ? path->len - (param - path->str) : 0), 0 }; + if (type == 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(liLogTarget); + log->type = type; + log->path = g_string_new_len(GSTR_LEN(path)); + log->wqelem.data = log; + switch (type) { case LI_LOG_TYPE_STDERR: fd = STDERR_FILENO; @@ -59,21 +77,14 @@ static liLogTarget *log_open(liServer *srv, GString *path) { fd = li_angel_fake_log_open_file(srv, &sparam); break; case LI_LOG_TYPE_PIPE: - ERROR(srv, "%s", "pipe logging not supported yet"); + li_angel_log_open_pipe(srv, &sparam, log_open_pipe_cb, log); break; case LI_LOG_TYPE_SYSLOG: 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(liLogTarget); - log->type = type; - log->path = g_string_new_len(GSTR_LEN(path)); log->fd = fd; - log->wqelem.data = log; li_radixtree_insert(srv->logs.targets, log->path->str, log->path->len * 8, log); /*g_print("log_open(\"%s\")\n", log->path->str);*/ } @@ -374,6 +385,8 @@ static void log_watcher_cb(struct ev_loop *loop, ev_async *w, int revents) { continue; } + if (log->type == LI_LOG_TYPE_PIPE) + log_close(srv, log); str = g_string_sized_new(63); g_string_printf(str, "could not write to log '%s': %s\n", log_entry->path->str, g_strerror(err)); li_log_write_stderr(srv, str->str, TRUE);