/* * mod_status - display server status * * Description: * mod_status can display a page with statistics like requests, traffic and active connections. * It can be customized with different stylesheets (css) * * Setups: * none * Options: * status.css - set the stylesheet to use, optional * type: string; values: "default", "blue" or a url to an external css file * Actions: * status.info - returns the status info page to the client * * Example config: * req.path == "/srv-status" { * status.css = "http://mydomain/status.css"; * status.info; * } * * Todo: * - add querystring parameter format=plain * * Author: * Copyright (c) 2008-2009 Thomas Porzelt * License: * MIT, see COPYING file in the lighttpd 2 tree */ #include #include #include LI_API gboolean mod_status_init(liModules *mods, liModule *mod); LI_API gboolean mod_status_free(liModules *mods, liModule *mod); static liHandlerResult status_info_runtime(liVRequest *vr, liPlugin *p); static gint str_comp(gconstpointer a, gconstpointer b); /* html snippet constants */ static const gchar header[] = "\n" "\n" "\n" " \n" " Lighttpd Status\n"; static const gchar html_top[] = "
Lighttpd Server Status | " " main - runtime" "
\n" "
\n" " Hostname: %s" " Uptime: %s\n" " Started at: %s\n" " Version: " PACKAGE_VERSION " (" __DATE__ " " __TIME__ ")\n" "
\n"; static const gchar html_worker_th[] = " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; static const gchar html_worker_th_avg[] = "
RequestsTraffic inTraffic outActive connections
\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; static const gchar html_worker_row[] = " \n" " \n" " \n" " \n" " \n" " \n" " \n"; static const gchar html_worker_row_avg[] = " \n" " \n" " \n" " \n" " \n" " \n" " \n"; static const gchar html_connections_th[] = "
Requests / sTraffic in / sTraffic out / sActive connections
%s%s (%" G_GUINT64_FORMAT "%%)%s (%" G_GUINT64_FORMAT "%%)%s (%" G_GUINT64_FORMAT "%%)%u (%u%%)
%s%s%s%s%u
\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; static const gchar html_connections_row[] = " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; static const gchar html_server_info[] = "
ClientStateHostPath+QuerystringDurationTraffic in/outTraffic in/out / sMethodRequest SizeResponse Size
%s%s%s%s%s%s%s%s / %s%s / %s%s%s%s
\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "
Hostname%s
User%s (%u)
Event handler%s
\n"; static const gchar html_libinfo_th[] = " \n" " \n" " \n" " \n" " \n" " \n"; static const gchar html_libinfo_row[] = " \n" " \n" " \n" " \n" " \n"; static const gchar html_libev_th[] = "
linkedheaders
%s%s%s
\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; static const gchar html_libev_row[] = " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; static const gchar css_default[] = " \n"; /* blue theme by nitrox */ static const gchar css_blue[] = " \n"; typedef struct mod_status_param mod_status_param; struct mod_status_param { liPlugin *p; gboolean short_info; /* no connection details */ }; typedef struct mod_status_wrk_data mod_status_wrk_data; typedef struct mod_status_con_data mod_status_con_data; typedef struct mod_status_job mod_status_job; struct mod_status_con_data { guint worker_ndx; liConnectionState state; GString *remote_addr_str, *local_addr_str; gboolean is_ssl, keep_alive; GString *host, *path, *query; liHttpMethod method; goffset request_size; goffset response_size; ev_tstamp ts; guint64 bytes_in; guint64 bytes_out; guint64 bytes_in_5s_diff; guint64 bytes_out_5s_diff; }; struct mod_status_wrk_data { guint worker_ndx; liStatistics stats; GArray *connections; }; struct mod_status_job { liVRequest *vr; gpointer *context; liPlugin *p; gboolean short_info; }; static gchar status_state_c(liConnectionState state) { static const gchar states[] = "dksrhw"; return states[state]; } /* the CollectFunc */ static gpointer status_collect_func(liWorker *wrk, gpointer fdata) { mod_status_wrk_data *sd = g_slice_new(mod_status_wrk_data); UNUSED(fdata); sd->stats = wrk->stats; sd->worker_ndx = wrk->ndx; /* gather connection info */ sd->connections = g_array_sized_new(FALSE, TRUE, sizeof(mod_status_con_data), wrk->connections_active); g_array_set_size(sd->connections, wrk->connections_active); for (guint i = 0; i < wrk->connections_active; i++) { liConnection *c = g_array_index(wrk->connections, liConnection*, i); mod_status_con_data *cd = &g_array_index(sd->connections, mod_status_con_data, i); cd->is_ssl = c->is_ssl; cd->keep_alive = c->keep_alive; cd->remote_addr_str = g_string_new_len(GSTR_LEN(c->remote_addr_str)); cd->local_addr_str = g_string_new_len(GSTR_LEN(c->local_addr_str)); cd->host = g_string_new_len(GSTR_LEN(c->mainvr->request.uri.host)); cd->path = g_string_new_len(GSTR_LEN(c->mainvr->request.uri.path)); cd->query = g_string_new_len(GSTR_LEN(c->mainvr->request.uri.query)); cd->method = c->mainvr->request.http_method; cd->request_size = c->mainvr->request.content_length; cd->response_size = c->mainvr->out->bytes_out; cd->state = c->state; cd->ts = c->ts; cd->bytes_in = c->stats.bytes_in; cd->bytes_out = c->stats.bytes_out; cd->bytes_in_5s_diff = c->stats.bytes_in_5s_diff; cd->bytes_out_5s_diff = c->stats.bytes_out_5s_diff; } return sd; } /* the CollectCallback */ static void status_collect_cb(gpointer cbdata, gpointer fdata, GPtrArray *result, gboolean complete) { mod_status_job *job = fdata; liVRequest *vr; liPlugin *p; UNUSED(cbdata); if (!complete) { /* someone called li_collect_break, so we don't need any vrequest handling here. just free the result data */ guint i, j; for (i = 0; i < result->len; i++) { mod_status_wrk_data *sd = g_ptr_array_index(result, i); for (j = 0; j < sd->connections->len; j++) { mod_status_con_data *cd = &g_array_index(sd->connections, mod_status_con_data, j); g_string_free(cd->remote_addr_str, TRUE); g_string_free(cd->local_addr_str, TRUE); g_string_free(cd->host, TRUE); g_string_free(cd->path, TRUE); } g_array_free(sd->connections, TRUE); g_slice_free(mod_status_wrk_data, sd); } g_slice_free(mod_status_job, job); return; } vr = job->vr; p = job->p; /* clear context so it doesn't get cleaned up anymore */ *(job->context) = NULL; g_slice_free(mod_status_job, job); if (complete) { GString *html; GString *tmpstr; GString *count_req, *count_bin, *count_bout; guint uptime; guint total_connections = 0; guint i; guint j; /* we got everything */ liStatistics totals = { G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), G_GUINT64_CONSTANT(0), 0, 0, G_GUINT64_CONSTANT(0), 0, 0 }; uptime = CUR_TS(vr->wrk) - vr->wrk->srv->started; if (!uptime) uptime = 1; html = g_string_sized_new(8 * 1024 - 1); count_req = g_string_sized_new(10); count_bin = g_string_sized_new(10); count_bout = g_string_sized_new(10); tmpstr = g_string_sized_new(10); VR_DEBUG(vr, "finished collecting data: %s", complete ? "complete" : "not complete"); /* calculate total stats over all workers */ for (i = 0; i < result->len; i++) { mod_status_wrk_data *sd = g_ptr_array_index(result, i); totals.bytes_out += sd->stats.bytes_out; totals.bytes_in += sd->stats.bytes_in; totals.requests += sd->stats.requests; totals.actions_executed += sd->stats.actions_executed; total_connections += sd->connections->len; totals.requests_5s_diff += sd->stats.requests_5s_diff; totals.bytes_in_5s_diff += sd->stats.bytes_in_5s_diff; totals.bytes_out_5s_diff += sd->stats.bytes_out_5s_diff; totals.active_cons_cum += sd->stats.active_cons_cum; totals.active_cons_5s += sd->stats.active_cons_5s; } g_string_append_len(html, header, sizeof(header)-1); /* auto refresh */ { gchar *val; guint len; gchar c; if (li_querystring_find(vr->request.uri.query, CONST_STR_LEN("refresh"), &val, &len)) { g_string_append_len(html, CONST_STR_LEN("\n")); } } /* css */ { GString *css = _OPTION(vr, p, 0).string; if (!css || !css->len) /* default css */ g_string_append_len(html, css_default, sizeof(css_default)-1); else if (g_str_equal(css->str, "blue")) /* blue css */ g_string_append_len(html, css_blue, sizeof(css_blue)-1); else /* external css */ g_string_append_printf(html, " \n", css->str); } g_string_append_len(html, CONST_STR_LEN( " \n" " \n" )); li_counter_format((guint64)(CUR_TS(vr->wrk) - vr->wrk->srv->started), COUNTER_TIME, tmpstr); g_string_append_printf(html, html_top, vr->request.uri.host->str, tmpstr->str, vr->wrk->srv->started_str->str ); /* worker information, absolute values */ { g_string_append_len(html, CONST_STR_LEN("
Absolute stats
\n")); g_string_append_len(html, html_worker_th, sizeof(html_worker_th)-1); #define PERCENTAGE(x, y) (y ? (x * 100 / y) : 0) for (i = 0; i < result->len; i++) { mod_status_wrk_data *sd = g_ptr_array_index(result, i); li_counter_format(sd->stats.requests, COUNTER_UNITS, count_req); li_counter_format(sd->stats.bytes_in, COUNTER_BYTES, count_bin); li_counter_format(sd->stats.bytes_out, COUNTER_BYTES, count_bout); g_string_printf(tmpstr, "Worker #%u", i+1); g_string_append_printf(html, html_worker_row, "", tmpstr->str, count_req->str, PERCENTAGE(sd->stats.requests, totals.requests), count_bin->str, PERCENTAGE(sd->stats.bytes_in, totals.bytes_in), count_bout->str, PERCENTAGE(sd->stats.bytes_out, totals.bytes_out), sd->connections->len, PERCENTAGE(sd->connections->len, total_connections)); } #undef PERCENTAGE li_counter_format(totals.requests, COUNTER_UNITS, count_req); li_counter_format(totals.bytes_in, COUNTER_BYTES, count_bin); li_counter_format(totals.bytes_out, COUNTER_BYTES, count_bout); g_string_append_printf(html, html_worker_row, "totals", "Total", count_req->str, G_GUINT64_CONSTANT(100), count_bin->str, G_GUINT64_CONSTANT(100), count_bout->str, G_GUINT64_CONSTANT(100), total_connections, 100); g_string_append_len(html, CONST_STR_LEN("
selectpollepollkqueue/dev/pollsolaris event port
%s%s%s%s%s%s%s
\n")); } /* worker information, avg values */ { g_string_append_len(html, CONST_STR_LEN("
Average stats (since start)
\n")); g_string_append_len(html, html_worker_th_avg, sizeof(html_worker_th_avg)-1); #define PERCENTAGE(x) (sd->stat ## x ? (sd->stat ## x * 100 / total ## x) : 0) for (i = 0; i < result->len; i++) { mod_status_wrk_data *sd = g_ptr_array_index(result, i); li_counter_format(sd->stats.requests / uptime, COUNTER_UNITS, count_req); li_counter_format(sd->stats.bytes_in / uptime, COUNTER_BYTES, count_bin); li_counter_format(sd->stats.bytes_out / uptime, COUNTER_BYTES, count_bout); g_string_printf(tmpstr, "Worker #%u", i+1); g_string_append_printf(html, html_worker_row_avg, "", tmpstr->str, count_req->str, count_bin->str, count_bout->str, (guint)(sd->stats.active_cons_cum / uptime) ); } #undef PERCENTAGE li_counter_format(totals.requests / uptime, COUNTER_UNITS, count_req); li_counter_format(totals.bytes_in / uptime, COUNTER_BYTES, count_bin); li_counter_format(totals.bytes_out / uptime, COUNTER_BYTES, count_bout); g_string_append_printf(html, html_worker_row_avg, "totals", "Total", count_req->str, count_bin->str, count_bout->str, (guint)(totals.active_cons_cum / uptime) ); g_string_append_len(html, CONST_STR_LEN(" \n")); } /* worker information, 5 seconds avg values */ { g_string_append_len(html, CONST_STR_LEN("
Average stats (5 seconds)
\n")); g_string_append_len(html, html_worker_th_avg, sizeof(html_worker_th_avg)-1); #define PERCENTAGE(x) (sd->stat ## x ? (sd->stat ## x * 100 / total ## x) : 0) for (i = 0; i < result->len; i++) { mod_status_wrk_data *sd = g_ptr_array_index(result, i); li_counter_format(sd->stats.requests_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_UNITS, count_req); li_counter_format(sd->stats.bytes_in_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, count_bin); li_counter_format(sd->stats.bytes_out_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, count_bout); g_string_printf(tmpstr, "Worker #%u", i+1); g_string_append_printf(html, html_worker_row_avg, "", tmpstr->str, count_req->str, count_bin->str, count_bout->str, sd->stats.active_cons_5s ); } #undef PERCENTAGE li_counter_format(totals.requests_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_UNITS, count_req); li_counter_format(totals.bytes_in_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, count_bin); li_counter_format(totals.bytes_out_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, count_bout); g_string_append_printf(html, html_worker_row_avg, "totals", "Total", count_req->str, count_bin->str, count_bout->str, totals.active_cons_5s ); g_string_append_len(html, CONST_STR_LEN(" \n")); } /* scoreboard */ { guint k = 0; g_string_append_printf(html, "
%u connections
\n
", total_connections); for (i = 0; i < result->len; i++) { mod_status_wrk_data *sd = g_ptr_array_index(result, i); for (j = 0; j < sd->connections->len; j++) { mod_status_con_data *cd = &g_array_index(sd->connections, mod_status_con_data, j); k++; if (k == 100) { g_string_append_len(html, CONST_STR_LEN("
\n")); k = 0; } g_string_append_c(html, status_state_c(cd->state)); } } g_string_append_len(html, CONST_STR_LEN("
\n
legend
\n
" "d = dead, k = keep-alive, s = request start, r = read request header, h = handle main vrequest, w = write" "
\n")); } /* list connections */ if (!job->short_info) { GString *ts, *bytes_in, *bytes_out, *bytes_in_5s, *bytes_out_5s; GString *req_len, *resp_len; guint len; ts = g_string_sized_new(15); bytes_in = g_string_sized_new(10); bytes_out = g_string_sized_new(10); bytes_in_5s = g_string_sized_new(10); bytes_out_5s = g_string_sized_new(10); req_len = g_string_sized_new(10); resp_len = g_string_sized_new(10); g_string_append_len(html, CONST_STR_LEN("
Active connections
\n")); g_string_append_len(html, html_connections_th, sizeof(html_connections_th)-1); for (i = 0; i < result->len; i++) { mod_status_wrk_data *sd = g_ptr_array_index(result, i); for (j = 0; j < sd->connections->len; j++) { mod_status_con_data *cd = &g_array_index(sd->connections, mod_status_con_data, j); li_counter_format((guint64)(CUR_TS(vr->wrk) - cd->ts), COUNTER_TIME, ts); li_counter_format(cd->bytes_in, COUNTER_BYTES, bytes_in); li_counter_format(cd->bytes_in_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, bytes_in_5s); li_counter_format(cd->bytes_out, COUNTER_BYTES, bytes_out); li_counter_format(cd->bytes_out_5s_diff / G_GUINT64_CONSTANT(5), COUNTER_BYTES, bytes_out_5s); li_counter_format(cd->request_size, COUNTER_BYTES, req_len); li_counter_format(cd->response_size, COUNTER_BYTES, resp_len); g_string_append_printf(html, html_connections_row, cd->remote_addr_str->str, li_connection_state_str(cd->state), cd->host->str, cd->path->str, cd->query->len ? "?":"", cd->query->len ? cd->query->str : "", ts->str, bytes_in->str, bytes_out->str, bytes_in_5s->str, bytes_out_5s->str, (cd->state >= LI_CON_STATE_HANDLE_MAINVR) ? li_http_method_string(cd->method, &len) : "", (cd->state >= LI_CON_STATE_HANDLE_MAINVR && cd->request_size != -1) ? req_len->str : "", (cd->state >= LI_CON_STATE_HANDLE_MAINVR) ? resp_len->str : "" ); g_string_free(cd->remote_addr_str, TRUE); g_string_free(cd->local_addr_str, TRUE); g_string_free(cd->host, TRUE); g_string_free(cd->path, TRUE); g_string_free(cd->query, TRUE); } g_array_free(sd->connections, TRUE); } g_string_append_len(html, CONST_STR_LEN(" \n")); g_string_free(ts, TRUE); g_string_free(bytes_in, TRUE); g_string_free(bytes_in_5s, TRUE); g_string_free(bytes_out, TRUE); g_string_free(bytes_out_5s, TRUE); g_string_free(req_len, TRUE); g_string_free(resp_len, TRUE); } else { for (i = 0; i < result->len; i++) { mod_status_wrk_data *sd = g_ptr_array_index(result, i); for (j = 0; j < sd->connections->len; j++) { mod_status_con_data *cd = &g_array_index(sd->connections, mod_status_con_data, j); g_string_free(cd->remote_addr_str, TRUE); g_string_free(cd->local_addr_str, TRUE); g_string_free(cd->host, TRUE); g_string_free(cd->path, TRUE); g_string_free(cd->query, TRUE); } g_array_free(sd->connections, TRUE); } } /* free stats */ for (i = 0; i < result->len; i++) { mod_status_wrk_data *sd = g_ptr_array_index(result, i); g_slice_free(mod_status_wrk_data, sd); } g_string_append_len(html, CONST_STR_LEN( " \n" "\n" )); li_chunkqueue_append_string(vr->out, html); li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); g_string_free(count_req, TRUE); g_string_free(count_bin, TRUE); g_string_free(count_bout, TRUE); g_string_free(tmpstr, TRUE); vr->response.http_status = 200; li_vrequest_joblist_append(vr); } else { /* something went wrong, client may have dropped the connection */ VR_ERROR(vr, "%s", "collect request didn't end up complete"); li_vrequest_error(vr); } } static liHandlerResult status_info(liVRequest *vr, gpointer _param, gpointer *context) { mod_status_param *param = _param; if (li_vrequest_handle_direct(vr)) { gchar *val; guint len; if (!li_querystring_find(vr->request.uri.query, CONST_STR_LEN("mode"), &val, &len)) { /* no 'mode' query parameter given */ liCollectInfo *ci; mod_status_job *j = g_slice_new(mod_status_job); j->vr = vr; j->context = context; j->p = param->p; j->short_info = param->short_info; VR_DEBUG(vr, "%s", "collecting stats..."); ci = li_collect_start(vr->wrk, status_collect_func, j, status_collect_cb, NULL); *context = ci; /* may be NULL */ return (*context) ? LI_HANDLER_WAIT_FOR_EVENT : LI_HANDLER_GO_ON; } else { /* 'mode' parameter given */ if (!param->short_info && strncmp(val, "runtime", len) == 0) { return status_info_runtime(vr, param->p); } else { vr->response.http_status = 403; } } } return LI_HANDLER_GO_ON; } static liHandlerResult status_info_cleanup(liVRequest *vr, gpointer param, gpointer context) { liCollectInfo *ci = (liCollectInfo*) context; UNUSED(vr); UNUSED(param); li_collect_break(ci); return LI_HANDLER_GO_ON; } static void status_info_free(liServer *srv, gpointer param) { UNUSED(srv); g_slice_free(mod_status_param, param); } static liAction* status_info_create(liServer *srv, liPlugin* p, liValue *val) { mod_status_param *param = g_slice_new0(mod_status_param); UNUSED(srv); UNUSED(val); param->p = p; param->short_info = FALSE; if (val) { if (val->type != LI_VALUE_STRING) { ERROR(srv, "%s", "status.info expects either a string or nothing as parameter"); goto error_free_param; } if (0 == strcmp(val->data.string->str, "short")) { param->short_info = TRUE; } else { ERROR(srv, "status.info: unexpected parameter '%s'", val->data.string->str); goto error_free_param; } } return li_action_new_function(status_info, status_info_cleanup, status_info_free, param); error_free_param: g_slice_free(mod_status_param, param); return NULL; } static gint str_comp(gconstpointer a, gconstpointer b) { return strcmp(*(const gchar**)a, *(const gchar**)b); } static liHandlerResult status_info_runtime(liVRequest *vr, liPlugin *p) { GString *html; html = g_string_sized_new(2047); g_string_append_len(html, CONST_STR_LEN(header)); /* auto refresh */ { gchar *val; guint len; gchar c; if (li_querystring_find(vr->request.uri.query, CONST_STR_LEN("refresh"), &val, &len)) { g_string_append_len(html, CONST_STR_LEN("\n")); } } /* css */ { GString* css = _OPTION(vr, p, 0).string; if (!css || !css->len) /* default css */ g_string_append_len(html, CONST_STR_LEN(css_default)); else if (g_str_equal(css->str, "blue")) /* blue css */ g_string_append_len(html, CONST_STR_LEN(css_blue)); else /* external css */ g_string_append_printf(html, " \n", css->str); } g_string_append_len(html, CONST_STR_LEN( " \n" " \n" )); li_counter_format((guint64)(CUR_TS(vr->wrk) - vr->wrk->srv->started), COUNTER_TIME, vr->wrk->tmp_str); g_string_append_printf(html, html_top, vr->request.uri.host->str, vr->wrk->tmp_str->str, vr->wrk->srv->started_str->str ); /* general info */ { g_string_append_len(html, CONST_STR_LEN("
Server info
\n")); g_string_append_printf(html, html_server_info, g_get_host_name(), g_get_user_name(), getuid(), li_ev_backend_string(ev_backend(vr->wrk->loop)) ); } /* library info */ { GString *tmp_str = g_string_sized_new(31); g_string_append_len(html, CONST_STR_LEN("
Libraries
\n")); g_string_append_len(html, CONST_STR_LEN(html_libinfo_th)); g_string_truncate(tmp_str, 0); g_string_append_printf(tmp_str, "%u.%u.%u", glib_major_version, glib_minor_version, glib_micro_version); g_string_truncate(vr->wrk->tmp_str, 0); g_string_append_printf(vr->wrk->tmp_str, "%u.%u.%u", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION); g_string_append_printf(html, html_libinfo_row, "GLib", tmp_str->str, vr->wrk->tmp_str->str ); g_string_truncate(tmp_str, 0); g_string_append_printf(tmp_str, "%u.%u", ev_version_major(), ev_version_minor()); g_string_truncate(vr->wrk->tmp_str, 0); g_string_append_printf(vr->wrk->tmp_str, "%u.%u", EV_VERSION_MAJOR, EV_VERSION_MINOR); g_string_append_printf(html, html_libinfo_row, "libev", tmp_str->str, vr->wrk->tmp_str->str ); g_string_append_len(html, CONST_STR_LEN(" \n")); g_string_free(tmp_str, TRUE); } /* libev info */ { guint i; g_string_append_len(html, CONST_STR_LEN("
libev backends
\n")); g_string_append_len(html, CONST_STR_LEN(html_libev_th)); /* supported backend, aka compiled in */ i = ev_supported_backends(); g_string_append_printf(html, html_libev_row, "supported", i & EVBACKEND_SELECT ? "yes" : "no", i & EVBACKEND_POLL ? "yes" : "no", i & EVBACKEND_EPOLL ? "yes" : "no", i & EVBACKEND_KQUEUE ? "yes" : "no", i & EVBACKEND_DEVPOLL ? "yes" : "no", i & EVBACKEND_PORT ? "yes" : "no" ); /* recommended backends */ i = ev_recommended_backends(); g_string_append_printf(html, html_libev_row, "recommended", i & EVBACKEND_SELECT ? "yes" : "no", i & EVBACKEND_POLL ? "yes" : "no", i & EVBACKEND_EPOLL ? "yes" : "no", i & EVBACKEND_KQUEUE ? "yes" : "no", i & EVBACKEND_DEVPOLL ? "yes" : "no", i & EVBACKEND_PORT ? "yes" : "no" ); g_string_append_len(html, CONST_STR_LEN(" \n")); } /* list modules */ { guint i, col; gpointer k, v; GHashTableIter iter; GArray *list = g_array_sized_new(FALSE, FALSE, sizeof(gchar*), g_hash_table_size(vr->wrk->srv->plugins)); g_string_append_len(html, CONST_STR_LEN("
Loaded modules
\n")); g_string_append_len(html, CONST_STR_LEN(" \n")); g_hash_table_iter_init(&iter, vr->wrk->srv->plugins); while (g_hash_table_iter_next(&iter, &k, &v)) g_array_append_val(list, k); g_array_sort(list, str_comp); col = 0; for (i = 1; i < list->len; i++) { if (col == 0) { g_string_append_len(html, CONST_STR_LEN(" \n")); col++; } else if (col == 5) { g_string_append_len(html, CONST_STR_LEN(" \n")); col = 0; continue; } else { col++; } g_string_append_printf(html, " \n", g_array_index(list, gchar*, i)); } if (col) { for (i = 5 - col; i; i--) g_string_append_len(html, CONST_STR_LEN(" \n")); g_string_append_len(html, CONST_STR_LEN(" \n")); } g_string_append_len(html, CONST_STR_LEN("
%s
\n")); g_array_free(list, TRUE); } /* list environment vars */ { gchar **e; gchar **env = g_listenv(); g_string_append_len(html, CONST_STR_LEN("
Process environment
\n")); g_string_append_len(html, CONST_STR_LEN(" \n")); for (e = env; *e; e++) { g_string_append_printf(html, " \n", *e, getenv(*e)); } g_string_append_len(html, CONST_STR_LEN("
%s%s
\n")); g_strfreev(env); } g_string_append_len(html, CONST_STR_LEN( " \n" "\n" )); li_chunkqueue_append_string(vr->out, html); li_http_header_overwrite(vr->response.headers, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); vr->response.http_status = 200; return LI_HANDLER_GO_ON; } static const liPluginOption options[] = { { "status.css", LI_VALUE_STRING, NULL, NULL, NULL }, { NULL, 0, NULL, NULL, NULL } }; static const liPluginAction actions[] = { { "status.info", status_info_create }, { NULL, NULL } }; static const liPluginSetup setups[] = { { NULL, NULL } }; static void plugin_status_init(liServer *srv, liPlugin *p) { UNUSED(srv); p->options = options; p->actions = actions; p->setups = setups; } gboolean mod_status_init(liModules *mods, liModule *mod) { UNUSED(mod); MODULE_VERSION_CHECK(mods); mod->config = li_plugin_register(mods->main, "mod_status", plugin_status_init); return mod->config != NULL; } gboolean mod_status_free(liModules *mods, liModule *mod) { UNUSED(mods); UNUSED(mod); if (mod->config) li_plugin_free(mods->main, mod->config); return TRUE; }