/* ############################################################################### mod_scoreboard.c - A module for Apache 1.3.29 Version 0.1 beta - 10/NOV/2003 - First public release (c) 2003 Juan R. Pozo http://html.conclase.net/cp/scripts/ mailto:jrpozo@conclase.net Mailing-list: http://www.conclase.net/mailman/listinfo/cpanel_conclase.net ############################################################################### This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################### INSTALLATION 1) Add the configuration directives to your httpd.conf (see below) 2) # make all 3) # make install 4) Stop Apache 5) Start Apache A small helper app in Perl is included as an example of how to handle the output of the scoreboard. Execute as: # watch -n5 ./scoreboard Note 1: this module requires Ralph Engelschall's MM library to be installed You may download it from http://www.ossp.org/pkg/lib/mm/ Note 2: this module has only been tested under Linux 2.4. It uses the /proc filesystem to gather uptime and cpu usage data ############################################################################### CONFIGURATION DIRECTIVES ModScoreboardFile /usr/local/apache/logs/scoreboard.dat SetHandler scoreboard Order deny,allow Deny from all Allow from 127.0.0.1 Restart and access through http://127.0.0.1/scoreboard ############################################################################### Changelog: 20/NOV/2003 - v. 0.1 First public release ############################################################################### Please consider making a donation today. Visit my amazon.com wishlist at: http://html.conclase.net/link/wishlist Thank you :) ############################################################################### */ #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_main.h" #include "http_protocol.h" #include "http_request.h" #include "scoreboard.h" #include "http_conf_globals.h" #include "mm.h" unsigned long int get_jiffies(int); unsigned long int uptime_jiffies(); static void stop_counting_time(void *); module MODULE_VAR_EXPORT scoreboard_module; /* SCOREBOARD DATA (IN SHARED MEMORY) */ typedef struct sb_entry { int pid; unsigned long int cpus; unsigned long int start; unsigned long int stop; char request[2048]; char hostname[32]; char status; } sb_entry; /* SCOREBOARD SERVER CONFIG */ typedef struct { char *filename; MM *mm; sb_entry *scoreboard; } scoreboard_conf; void *scoreboard_server_conf(pool *p, server_rec *s) { scoreboard_conf *scfg = (scoreboard_conf *)ap_palloc(p, sizeof(scoreboard_conf)); scfg->filename = NULL; scfg->mm = NULL; scfg->scoreboard = NULL; return (void *)scfg; } /* COMMAND TABLE */ static const char *scoreboard_set_filename( cmd_parms *parms, void *mconfig, const char *arg ) { scoreboard_conf *scfg = (scoreboard_conf *) ap_get_module_config(parms->server->module_config, &scoreboard_module); scfg->filename = ap_pstrdup(parms->pool, arg); return NULL; } command_rec scoreboard_cmds[] = { { "ModScoreboardFile", scoreboard_set_filename, NULL, RSRC_CONF, TAKE1, "the absolute path to the scoreboard file" }, { NULL } }; /* MODULE INIT */ void scoreboard_init(server_rec *s, pool *p) { size_t size = HARD_SERVER_LIMIT * (sizeof(sb_entry)+16); scoreboard_conf *scfg = (scoreboard_conf *) ap_get_module_config(s->module_config, &scoreboard_module); int i; if (!scfg->filename) { fprintf(stderr, "httpd: missing scoreboard filename\n"); exit(1); } scfg->mm = mm_create(size, scfg->filename); if (!scfg->mm) { fprintf(stderr, "httpd: could not setup shared memory for scoreboard\n"); exit(1); } mm_permission(scfg->mm, S_IRUSR|S_IWUSR, ap_user_id, ap_group_id); scfg->scoreboard = (sb_entry *) mm_calloc(scfg->mm, HARD_SERVER_LIMIT, sizeof(sb_entry)); if (!scfg->scoreboard) { fprintf(stderr, "httpd: could not allocate shared memory for scoreboard\n"); exit(1); } } /* PROCESS INIT */ void scoreboard_child_init(server_rec *s, pool *p) { scoreboard_conf *scfg = (scoreboard_conf *) ap_get_module_config(s->module_config, &scoreboard_module); int i; for (i = 0; i < HARD_SERVER_LIMIT; i++) { if (!scfg->scoreboard[i].pid) { scfg->scoreboard[i].pid = (int)getpid(); scfg->scoreboard[i].cpus = 0; scfg->scoreboard[i].start = 0; scfg->scoreboard[i].stop = 0; scfg->scoreboard[i].request[0] = 0; scfg->scoreboard[i].hostname[0] = 0; scfg->scoreboard[i].status = 'S'; return; } } } /* CHILD TERMINATION */ void scoreboard_child_exit(server_rec *s, pool *p) { scoreboard_conf *scfg = (scoreboard_conf *) ap_get_module_config(s->module_config, &scoreboard_module); int i, pid; for (i = 0, pid = (int)getpid(); i < HARD_SERVER_LIMIT; i++) { if (scfg->scoreboard[i].pid == pid) { scfg->scoreboard[i].pid = 0; return; } } } /* REQUEST INIT (actually, post read request phase) */ static int scoreboard_request_init(request_rec *r) { scoreboard_conf *scfg = (scoreboard_conf *) ap_get_module_config(r->server->module_config, &scoreboard_module); int i, pid; request_rec *rr; sb_entry *entry; for (i = 0, pid = (int)getpid(); i < HARD_SERVER_LIMIT; i++) { if (scfg->scoreboard[i].pid == pid) { entry = &scfg->scoreboard[i]; rr = r->main ? r->main : r; entry->request[0] = 0; if (rr->the_request && strlen(rr->the_request)) { strncpy(entry->request, rr->the_request, 2048); } else if (rr->unparsed_uri && strlen(rr->unparsed_uri)) { strncpy(entry->request, rr->unparsed_uri, 2048); } entry->hostname[0] = 0; if (rr->parsed_uri.hostname && strlen(rr->parsed_uri.hostname)) { strncpy(entry->hostname, rr->parsed_uri.hostname, 32); } else if (rr->hostname && rr->hostname && strlen(rr->hostname)) { strncpy(entry->hostname, rr->hostname, 32); } else if (rr->server && rr->server->server_hostname && strlen(rr->server->server_hostname)) { strncpy(entry->hostname, rr->server->server_hostname, 32); } entry->cpus = get_jiffies((int)getpid()); entry->start = uptime_jiffies(); entry->stop = 0; entry->status = 'R'; ap_register_cleanup(r->pool, (void *)entry, stop_counting_time, ap_null_cleanup); return OK; } } return OK; } unsigned long int get_jiffies(int pid) { char file[50], stat[1024]; char *ucpu, *scpu; FILE *f; int i; snprintf(file, 50, "/proc/%d/stat", pid); f = fopen(file, "r"); if (!f) { return 0; } fgets(stat, 1024, f); fclose(f); strtok(stat, " "); for (i = 0; i < 13; i++) { ucpu = strtok(NULL, " "); } scpu = strtok(NULL, " "); strtok(NULL, " "); return (unsigned long int)(atol(ucpu) + atol(scpu)); } unsigned long int uptime_jiffies() { char stat[1024]; char *ent, *dec; FILE *f; f = fopen("/proc/uptime", "r"); if (!f) { return 0; } fgets(stat, 1024, f); fclose(f); ent = strtok(stat, "."); dec = strtok(NULL, " "); return (unsigned long int)(100*atol(ent) + atol(dec)); } /* THE HANDLERS PHASE (OUTPUT TABLE) */ int scoreboard_handler(request_rec *r) { int p; sb_entry *entry; unsigned long int delta_cpus, delta_time; scoreboard_conf *scfg = (scoreboard_conf *) ap_get_module_config(r->server->module_config, &scoreboard_module); r->allowed = (1 << M_GET); if (r->method_number != M_GET) { return DECLINED; } r->content_type = "text/plain"; ap_table_set(r->headers_out, "Connection", "close"); ap_send_http_header(r); if (r->header_only) { return OK; } for (p = 0; p < HARD_SERVER_LIMIT; p++) { /* Output format: pid status hostname cpu_time real_time request (times in jiffies) */ entry = &scfg->scoreboard[p]; if (!entry->pid) { continue; } ap_rprintf(r, "%d ", entry->pid); ap_rprintf(r, "%c ", entry->status); ap_rprintf(r, "%s ", entry->hostname ? entry->hostname : "?"); delta_cpus = get_jiffies((int)entry->pid) - entry->cpus; ap_rprintf(r, "%d ", delta_cpus); if (entry->start) { if (entry->stop) { delta_time = entry->stop - entry->start; } else { delta_time = uptime_jiffies() - entry->start; } ap_rprintf(r, "%d ", delta_time); } else { ap_rprintf(r, "0 "); } ap_rprintf(r, "%s\n", entry->request ? entry->request : "?"); } return OK; } /* THE HANDLERS TABLE */ static handler_rec scoreboard_handlers[] = { {"scoreboard", scoreboard_handler}, {NULL} }; /* CLEANUP PHASE */ static void stop_counting_time(void *data) { sb_entry *entry = (sb_entry *)data; entry->stop = uptime_jiffies(); entry->status = 'S'; } /* Finally the module structure */ module MODULE_VAR_EXPORT scoreboard_module = { STANDARD_MODULE_STUFF, scoreboard_init, /* module initializer */ NULL, /* per-directory config creator */ NULL, /* dir config merger */ scoreboard_server_conf, /* server config creator */ NULL, /* server config merger */ scoreboard_cmds, /* config directive table */ scoreboard_handlers, /* [9] content handlers */ NULL, /* [2] URI-to-filename translation */ NULL, /* [5] check/validate user_id */ NULL, /* [6] check user_id is valid *here* */ NULL, /* [4] check access by host address */ NULL, /* [7] MIME type checker/setter */ NULL, /* [8] fixups */ NULL, /* [10] logger */ NULL, /* [3] header parser */ scoreboard_child_init, /* process initialization */ scoreboard_child_exit, /* process exit/cleanup */ scoreboard_request_init /* [1] post read_request handling */ };