/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * Portions of this software are based upon public domain software * originally written at the National Center for Supercomputing Applications, * University of Illinois, Urbana-Champaign. */ /* XML Status Module. Display lots of internal data about how Apache is * performing and the state of all children processes. We do this * separately to mod_status for readability * * To enable this, add the following lines into any config file: * * * SetHandler server-status-xml * StatusXMLStylesheet /scoreboard.xsl * * * The StatusXMLStylesheet directive is optional and if set allows * browsers that support it to fetch a stylesheet which is used * to then display the XML data * * We assume you've also compiled in mod_status so you can use * the 'ExtendedStatus On' directive * * Use ?minimal if you don't want the per-child information or * things you can find out by calculation yourself * * Written by Mark J Cox, mjc@apache.org, August 2001 * Based on mod_status, November 1995 */ #define CORE_PRIVATE #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_protocol.h" #include "http_conf_globals.h" /* for ap_extended_status */ #include "http_main.h" #include "util_script.h" #include #include "scoreboard.h" #include "http_log.h" #ifdef NEXT #if (NX_CURRENT_COMPILER_RELEASE == 410) #ifdef m68k #define HZ 64 #else #define HZ 100 #endif #else #include #endif #endif /* NEXT */ #define KBYTE 1024 module MODULE_VAR_EXPORT status_xml_module; typedef struct { char *stylesheet_name; } status_xml_dir_rec; /* ID values for command table */ #define STAT_OPT_END -1 #define STAT_OPT_MINIMAL 0 struct stat_opt { int id; const char *form_data_str; const char *hdr_out_str; }; static const struct stat_opt status_options[] = /* see #defines above */ { {STAT_OPT_MINIMAL, "minimal", NULL}, {STAT_OPT_END, NULL, NULL} }; static char status_flags[SERVER_NUM_STATUS]; static int status_xml_handler(request_rec *r) { char *loc; time_t nowtime = time(NULL); time_t up_time; int i, res; int ready = 0; int busy = 0; unsigned long count = 0; unsigned long lres, bytes; unsigned long my_lres, my_bytes, conn_bytes; unsigned short conn_lres; unsigned long bcount = 0; unsigned long kbcount = 0; long req_time; #ifndef NO_TIMES #ifdef _SC_CLK_TCK float tick = sysconf(_SC_CLK_TCK); #else float tick = HZ; #endif #endif int minimal_report = 0; short_score score_record; parent_score ps_record; char stat_buffer[HARD_SERVER_LIMIT]; int pid_buffer[HARD_SERVER_LIMIT]; clock_t tu, ts, tcu, tcs; server_rec *vhost; status_xml_dir_rec *dcfg; dcfg = ap_get_module_config(r->per_dir_config, &status_xml_module); tu = ts = tcu = tcs = 0; if (!ap_exists_scoreboard_image()) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Server status unavailable in inetd mode"); return HTTP_INTERNAL_SERVER_ERROR; } r->allowed = (1 << M_GET); if (r->method_number != M_GET) return DECLINED; r->content_type = "text/xml"; if (r->args) { i = 0; while (status_options[i].id != STAT_OPT_END) { if ((loc = strstr(r->args, status_options[i].form_data_str)) != NULL) { switch (status_options[i].id) { case STAT_OPT_MINIMAL: minimal_report = 1; } } i++; } } ap_send_http_header(r); #ifdef CHARSET_EBCDIC /* Server-generated response, converted */ ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, r->ebcdic.conv_out = 1); #endif if (r->header_only) return 0; ap_sync_scoreboard_image(); for (i = 0; i < HARD_SERVER_LIMIT; ++i) { score_record = ap_scoreboard_image->servers[i]; ps_record = ap_scoreboard_image->parent[i]; res = score_record.status; stat_buffer[i] = status_flags[res]; pid_buffer[i] = (int) ps_record.pid; if (res == SERVER_READY) ready++; else if (res != SERVER_DEAD) busy++; if (ap_extended_status) { lres = score_record.access_count; bytes = score_record.bytes_served; if (lres != 0 || (res != SERVER_READY && res != SERVER_DEAD)) { #ifndef NO_TIMES tu += score_record.times.tms_utime; ts += score_record.times.tms_stime; tcu += score_record.times.tms_cutime; tcs += score_record.times.tms_cstime; #endif /* NO_TIMES */ count += lres; bcount += bytes; if (bcount >= KBYTE) { kbcount += (bcount >> 10); bcount = bcount & 0x3ff; } } } } up_time = nowtime - ap_restart_time; ap_hard_timeout("send xml status info", r); ap_rputs("\n\n",r); if (dcfg->stylesheet_name) ap_rvputs(r,"stylesheet_name, "\" type=\"text/xsl\"?>\n",NULL); ap_rprintf(r, "pool, nowtime, "time=\"%Y%m%d%H%M%S%Z\">\n", 0), NULL); ap_rvputs(r, ap_ht_time(r->pool, ap_restart_time, " %Y%m%d%H%M%S%Z\n", 0), NULL); ap_rprintf(r, " %ld\n",up_time); ap_rvputs(r, " ",ap_get_server_version(), "\n", NULL); ap_rprintf(r, " %d\n", (int)ap_my_generation); ap_rprintf(r, " \n", busy, ready, HARD_SERVER_LIMIT-1); if (ap_extended_status) { ap_rprintf(r, " %lu\n",count); ap_rprintf(r, " %lu\n", kbcount); #ifndef NO_TIMES if (ts || tu || tcu || tcs) ap_rprintf(r, " \n", tu/tick, ts/tick, tcu/tick, tcs/tick, (tu + ts + tcu + tcs) / tick / up_time * 100.); #endif if (!minimal_report && (up_time > 0 || count > 0)) { ap_rputs(" \n",r); if (up_time > 0) { ap_rprintf(r, " %g\n", (float) count / (float) up_time); ap_rprintf(r, " %g\n", KBYTE * (float) kbcount / (float) up_time); } if (count > 0) ap_rprintf(r, " %g\n", KBYTE * (float) kbcount / (float) count); ap_rputs(" \n",r); } } ap_rputs(" ", r); for (i = 0; i < HARD_SERVER_LIMIT; ++i) { ap_rputc(stat_buffer[i], r); } ap_rputs(" \n", r); if (ap_extended_status && !minimal_report) { ap_rputs(" \n",r); for (i = 0; i < HARD_SERVER_LIMIT; ++i) { score_record = ap_scoreboard_image->servers[i]; ps_record = ap_scoreboard_image->parent[i]; vhost = score_record.vhostrec; if (ps_record.generation != ap_my_generation) vhost = NULL; #if defined(NO_GETTIMEOFDAY) #ifndef NO_TIMES if (score_record.start_time == (clock_t) 0) #endif /* NO_TIMES */ req_time = 0L; #ifndef NO_TIMES else { req_time = score_record.stop_time - score_record.start_time; req_time = (req_time * 1000) / (int) tick; } #endif /* NO_TIMES */ #else if (score_record.start_time.tv_sec == 0L && score_record.start_time.tv_usec == 0L) req_time = 0L; else req_time = ((score_record.stop_time.tv_sec - score_record.start_time.tv_sec) * 1000) + ((score_record.stop_time.tv_usec - score_record.start_time.tv_usec) / 1000); #endif if (req_time < 0L) req_time = 0L; lres = score_record.access_count; my_lres = score_record.my_access_count; conn_lres = score_record.conn_count; bytes = score_record.bytes_served; my_bytes = score_record.my_bytes_served; conn_bytes = score_record.conn_bytes; if (lres != 0 || (score_record.status != SERVER_READY && score_record.status != SERVER_DEAD)) { ap_rprintf(r, " \n",r); ap_rprintf(r, " \n", (int) conn_lres, my_lres, lres); ap_rprintf(r, " %d\n", (int) ps_record.generation); #ifndef NO_TIMES ap_rprintf(r, " \n", score_record.times.tms_utime / tick, score_record.times.tms_stime / tick, score_record.times.tms_cutime / tick, score_record.times.tms_cstime / tick, (score_record.times.tms_utime + score_record.times.tms_stime + score_record.times.tms_cutime + score_record.times.tms_utime) / tick / up_time *100.); #endif ap_rprintf(r, " %.0f\n", #ifdef OPTIMIZE_TIMEOUTS difftime(nowtime, ps_record.last_rtime)); #else difftime(nowtime, score_record.last_used)); #endif ap_rprintf(r," %ld\n",(long)req_time); ap_rprintf(r," %ld\n",conn_bytes); ap_rprintf(r," %ld\n",my_bytes); ap_rprintf(r," %ld\n",bytes); ap_rprintf(r," %s\n", ap_escape_html(r->pool, score_record.client)); ap_rprintf(r," %s\n", ap_escape_html(r->pool, score_record.request)); if (vhost) ap_rprintf(r, " %s\n", ap_escape_html(r->pool, vhost->server_hostname)); #ifdef EAPI ap_rputs(" ",r); ap_hook_use("ap::mod_ssl::vendor::get_scoreboard_string", AP_HOOK_SIG3(void,ptr,ptr), AP_HOOK_MODE_ALL, &score_record, r); ap_rputs(" \n",r); #endif /* EAPI */ ap_rputs(" \n",r); } } ap_rputs(" \n",r); } ap_rputs("\n",r); ap_kill_timeout(r); return 0; } static void status_xml_init(server_rec *s, pool *p) { status_flags[SERVER_DEAD] = '.'; /* We don't want to assume these are in */ status_flags[SERVER_READY] = '_'; /* any particular order in scoreboard.h */ status_flags[SERVER_STARTING] = 'S'; status_flags[SERVER_BUSY_READ] = 'R'; status_flags[SERVER_BUSY_WRITE] = 'W'; status_flags[SERVER_BUSY_KEEPALIVE] = 'K'; status_flags[SERVER_BUSY_LOG] = 'L'; status_flags[SERVER_BUSY_DNS] = 'D'; status_flags[SERVER_GRACEFUL] = 'G'; } static void *make_status_xml_dir(pool *p, char *d) { status_xml_dir_rec *dcfg; dcfg = (status_xml_dir_rec *) ap_pcalloc(p, sizeof(status_xml_dir_rec)); dcfg->stylesheet_name = NULL; return dcfg; } static const char *set_stylesheet(cmd_parms *cmd, status_xml_dir_rec *dconf, char *name) { dconf->stylesheet_name = name; return NULL; } static const handler_rec status_xml_handlers[] = { {"server-status-xml", status_xml_handler}, {NULL} }; static const command_rec status_xml_cmds[] = { {"StatusXMLStylesheet", set_stylesheet, NULL, OR_INDEXES, TAKE1, "href to the location of the XSLT for processing XML status"}, {NULL} }; module MODULE_VAR_EXPORT status_xml_module = { STANDARD_MODULE_STUFF, status_xml_init, /* initializer */ make_status_xml_dir, /* dir config creater */ NULL, /* dir merger --- default is to override */ NULL, /* server config */ NULL, /* merge server config */ status_xml_cmds, /* command table */ status_xml_handlers, /* handlers */ NULL, /* filename translation */ NULL, /* check_user_id */ NULL, /* check auth */ NULL, /* check access */ NULL, /* type_checker */ NULL, /* fixups */ NULL, /* logger */ NULL, /* header parser */ NULL, /* child_init */ NULL, /* child_exit */ NULL /* post read-request */ };