blob: 316d61138c362c2b0a208a8e5c51703baa98a7eb [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/***************************************************************************
* Description: Status worker, display and manages JK workers *
* Author: Mladen Turk <mturk@jboss.com> *
* Author: Rainer Jung <rjung@apache.org> *
* Version: $Revision$ *
***************************************************************************/
#include "jk_pool.h"
#include "jk_service.h"
#include "jk_util.h"
#include "jk_worker.h"
#include "jk_status.h"
#include "jk_mt.h"
#include "jk_shm.h"
#include "jk_ajp_common.h"
#include "jk_lb_worker.h"
#include "jk_ajp13_worker.h"
#include "jk_ajp14_worker.h"
#include "jk_connect.h"
#include "jk_uri_worker_map.h"
#define HUGE_BUFFER_SIZE (8*1024)
#define JK_STATUS_HEAD "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
"DTD HTML 3.2 Final//EN\">\n" \
"<html><head><title>JK Status Manager</title>"
#define JK_STATUS_HEND "</head>\n<body>\n"
#define JK_STATUS_BEND "</body>\n</html>\n"
#define JK_STATUS_XMLH "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
"<jk:status xmlns:jk=\"http://tomcat.apache.org\">\n"
#define JK_STATUS_XMLE "</jk:status>\n"
#define JK_STATUS_TEXTUPDATE_RESPONCE "OK - jk status worker updated\n"
typedef struct status_worker status_worker_t;
struct status_endpoint
{
jk_endpoint_t *e;
status_worker_t *s_worker;
jk_endpoint_t endpoint;
};
typedef struct status_endpoint status_endpoint_t;
struct status_worker
{
jk_pool_t p;
jk_pool_atom_t buf[TINY_POOL_SIZE];
const char *name;
const char *css;
jk_worker_t worker;
status_endpoint_t ep;
jk_worker_env_t *we;
};
static const char *worker_type[] = {
"unknown",
"ajp12",
"ajp13",
"ajp14",
"jni",
"lb",
"status",
NULL
};
static const char *headers_names[] = {
"Content-Type",
"Cache-Control",
"Pragma",
NULL
};
#define HEADERS_NO_CACHE "no-cache", "no-cache", NULL
static const char *headers_vhtml[] = {
"text/html",
HEADERS_NO_CACHE
};
static const char *headers_vxml[] = {
"text/xml",
HEADERS_NO_CACHE
};
static const char *headers_vtxt[] = {
"text/plain",
HEADERS_NO_CACHE
};
#if !defined(HAVE_VSNPRINTF) && !defined(HAVE_APR)
static FILE *f = NULL;
static int vsnprintf(char *str, size_t n, const char *fmt, va_list ap)
{
int res;
if (f == NULL)
f = fopen("/dev/null", "w");
if (f == NULL)
return -1;
setvbuf(f, str, _IOFBF, n);
res = vfprintf(f, fmt, ap);
if (res > 0 && res < n) {
res = vsprintf(str, fmt, ap);
}
return res;
}
#endif
static int jk_printf(jk_ws_service_t *s, const char *fmt, ...)
{
int rc = 0;
va_list args;
#ifdef NETWARE
/* On NetWare, this can get called on a thread that has a limited stack so */
/* we will allocate and free the temporary buffer in this function */
char *buf;
#else
char buf[HUGE_BUFFER_SIZE];
#endif
if (!s || !fmt) {
return -1;
}
va_start(args, fmt);
#ifdef NETWARE
buf = (char *)malloc(HUGE_BUFFER_SIZE);
if (NULL == buf)
return -1;
#endif
#ifdef USE_VSPRINTF /* until we get a vsnprintf function */
rc = vsprintf(buf, fmt, args);
#else
rc = vsnprintf(buf, HUGE_BUFFER_SIZE, fmt, args);
#endif
va_end(args);
if (rc > 0)
s->write(s, buf, rc);
#ifdef NETWARE
free(buf);
#endif
return rc;
}
/* Actually APR's apr_strfsize */
static char *status_strfsize(jk_uint64_t size, char *buf)
{
const char ord[] = "KMGTPE";
const char *o = ord;
unsigned int remain, siz;
if (size < 973) {
if (sprintf(buf, "%3d ", (int) size) < 0)
return strcpy(buf, "****");
return buf;
}
do {
remain = (unsigned int)(size & 0x03FF);
size >>= 10;
if (size >= 973) {
++o;
continue;
}
siz = (unsigned int)(size & 0xFFFF);
if (siz < 9 || (siz == 9 && remain < 973)) {
if ((remain = ((remain * 5) + 256) / 512) >= 10)
++siz, remain = 0;
if (sprintf(buf, "%d.%d%c", siz, remain, *o) < 0)
return strcpy(buf, "****");
return buf;
}
if (remain >= 512)
++siz;
if (sprintf(buf, "%3d%c", siz, *o) < 0)
return strcpy(buf, "****");
return buf;
} while (1);
}
static const char *status_worker_type(int t)
{
if (t < 0 || t > 6)
t = 0;
return worker_type[t];
}
static const char *status_val_bool(int v)
{
if (v == 0)
return "False";
else
return "True";
}
static const char *status_val_status(int s, int d, int e, int r, int b)
{
if (s)
return "Stopped";
else if (d)
return "Disabled";
else if (r)
return "Recovering";
else if (e)
return "Error";
else if (b)
return "Busy";
else
return "OK";
}
static const char *status_val_match(unsigned int match)
{
if (match & MATCH_TYPE_STOPPED)
return "Stopped";
else if (match & MATCH_TYPE_DISABLED)
return "Disabled";
else if (match & MATCH_TYPE_NO_MATCH)
return "Unmount";
else if (match & MATCH_TYPE_EXACT)
return "Exact";
else if (match & MATCH_TYPE_CONTEXT)
return "Context";
else if (match & MATCH_TYPE_CONTEXT_PATH)
return "Context Path";
else if (match & MATCH_TYPE_SUFFIX)
return "Suffix";
else if (match & MATCH_TYPE_GENERAL_SUFFIX)
return "General Suffix";
else if (match & MATCH_TYPE_WILDCHAR_PATH)
return "Wildchar";
else
return "Error";
}
static void jk_puts(jk_ws_service_t *s, const char *str)
{
if (str)
s->write(s, str, (unsigned int)strlen(str));
else
s->write(s, "(null)", 6);
}
static void jk_putv(jk_ws_service_t *s, ...)
{
va_list va;
const char *str;
va_start(va, s);
while (1) {
str = va_arg(va, const char *);
if (str == NULL)
break;
s->write(s, str, (unsigned int)strlen(str));
}
va_end(va);
}
static const char *status_cmd(const char *param, const char *req, char *buf, size_t len)
{
char ps[32];
char *p;
size_t l = 0;
*buf = '\0';
if (!req)
return NULL;
if (!param)
return NULL;
sprintf(ps, "&%s=", param);
p = strstr(req, ps);
if (!p) {
sprintf(ps, "%s=", param);
if (!strncmp(req, ps, strlen(ps)))
p = (char *)req;
}
if (p) {
p += strlen(ps);
while (*p) {
if (*p != '&')
buf[l++] = *p;
else
break;
if (l + 2 > len)
break;
p++;
}
buf[l] = '\0';
if (l)
return buf;
else
return NULL;
}
else
return NULL;
}
static int status_int(const char *param, const char *req, int def)
{
const char *v;
char buf[32];
int rv = def;
if ((v = status_cmd(param, req, buf, sizeof(buf) -1))) {
rv = atoi(v);
}
return rv;
}
static int status_bool(const char *param, const char *req)
{
const char *v;
char buf[32];
int rv = 0;
if ((v = status_cmd(param, req, buf, sizeof(buf)))) {
if (strcasecmp(v, "on") == 0 ||
strcasecmp(v, "true") == 0)
rv = 1;
}
return rv;
}
static void display_maps(jk_ws_service_t *s, status_worker_t *sw,
jk_uri_worker_map_t *uwmap,
const char *worker, jk_logger_t *l)
{
unsigned int i;
jk_puts(s, "<br/>Uri Mappings:\n");
jk_puts(s, "<table>\n<tr><th>Match Type</th><th>Uri</th>"
"<th>Context</th></tr>\n");
for (i = 0; i < uwmap->size; i++) {
uri_worker_record_t *uwr = uwmap->maps[i];
if (strcmp(uwr->worker_name, worker)) {
continue;
}
jk_putv(s, "<tr><td>",
status_val_match(uwr->match_type),
"</td><td>", NULL);
jk_puts(s, uwr->uri);
jk_putv(s, "</td><td>", uwr->context, NULL);
jk_puts(s, "</td></tr>\n");
}
jk_puts(s, "</table>\n");
}
static void dump_maps(jk_ws_service_t *s, status_worker_t *sw,
jk_uri_worker_map_t *uwmap,
const char *worker, jk_logger_t *l)
{
unsigned int i;
for (i = 0; i < uwmap->size; i++) {
uri_worker_record_t *uwr = uwmap->maps[i];
if (strcmp(uwr->worker_name, worker)) {
continue;
}
jk_printf(s, " <jk:map type=\"%s\" uri=\"%s\" context=\"%s\" />\n",
status_val_match(uwr->match_type),
uwr->uri,
uwr->context) ;
}
}
/**
* Command line reference:
* cmd=list (default) display configuration
* cmd=show display detailed configuration
* cmd=update update configuration
* cmd=add add new uri map.
* w=worker display detailed configuration for worker
*
* Worker parameters:
* r=string redirect route name
*
*/
static void display_workers(jk_ws_service_t *s, status_worker_t *sw,
const char *dworker, jk_logger_t *l)
{
unsigned int i;
char buf[32];
for (i = 0; i < sw->we->num_of_workers; i++) {
jk_worker_t *w = wc_get_worker_for_name(sw->we->worker_list[i], l);
ajp_worker_t *aw = NULL;
lb_worker_t *lb = NULL;
if (w == NULL)
continue;
if (w->type == JK_LB_WORKER_TYPE) {
lb = (lb_worker_t *)w->worker_private;
}
else if (w->type == JK_AJP13_WORKER_TYPE ||
w->type == JK_AJP14_WORKER_TYPE) {
aw = (ajp_worker_t *)w->worker_private;
}
else {
/* Skip status, jni and ajp12 worker */
continue;
}
jk_puts(s, "<hr/>\n<h3>Worker Status for ");
if (dworker && strcmp(dworker, sw->we->worker_list[i]) == 0) {
/* Next click will colapse the editor */
jk_putv(s, "<a href=\"", s->req_uri, "?cmd=show\">", NULL);
}
else
jk_putv(s, "<a href=\"", s->req_uri, "?cmd=show&w=",
sw->we->worker_list[i], "\">", NULL);
jk_putv(s, sw->we->worker_list[i], "</a></h3>\n", NULL);
if (lb != NULL) {
unsigned int j;
int selected = -1;
jk_puts(s, "<table><tr>"
"<th>Type</th><th>Sticky session</th>"
"<th>Force Sticky session</th>"
"<th>Retries</th>"
"<th>Method</th>"
"<th>Lock</th>"
"</tr>\n<tr>");
jk_putv(s, "<td>", status_worker_type(w->type), "</td>", NULL);
jk_putv(s, "<td>", status_val_bool(lb->s->sticky_session),
"</td>", NULL);
jk_putv(s, "<td>", status_val_bool(lb->s->sticky_session_force),
"</td>", NULL);
jk_printf(s, "<td>%d</td>", lb->s->retries);
jk_printf(s, "<td>%s</td>", lb_method_type[lb->lbmethod]);
jk_printf(s, "<td>%s</td>", lb_locking_type[lb->lblock]);
jk_puts(s, "</tr>\n</table>\n<br/>\n");
jk_puts(s, "<table><tr>"
"<th>Name</th><th>Type</th><th>jvmRoute</th><th>Host</th><th>Addr</th>"
"<th>Stat</th><th>D</th><th>F</th><th>M</th><th>V</th><th>Acc</th><th>Err</th>"
"<th>Wr</th><th>Rd</th><th>Busy</th><th>Max</th><th>RR</th><th>Cd</th></tr>\n");
for (j = 0; j < lb->num_of_workers; j++) {
worker_record_t *wr = &(lb->lb_workers[j]);
ajp_worker_t *a = (ajp_worker_t *)wr->w->worker_private;
jk_putv(s, "<tr>\n<td><a href=\"", s->req_uri,
"?cmd=show&w=",
wr->s->name, "\">",
wr->s->name, "</a></td>", NULL);
if (dworker && strcmp(dworker, wr->s->name) == 0)
selected = j;
jk_putv(s, "<td>", status_worker_type(wr->w->type), "</td>", NULL);
jk_putv(s, "<td>", wr->s->jvm_route, "</td>", NULL);
jk_printf(s, "<td>%s:%d</td>", a->host, a->port);
jk_putv(s, "<td>", jk_dump_hinfo(&a->worker_inet_addr, buf),
"</td>", NULL);
/* TODO: descriptive status */
jk_putv(s, "<td>",
status_val_status(wr->s->is_stopped,
wr->s->is_disabled,
wr->s->in_error_state,
wr->s->in_recovering,
wr->s->is_busy),
"</td>", NULL);
jk_printf(s, "<td>%d</td>", wr->s->distance);
jk_printf(s, "<td>%d</td>", wr->s->lb_factor);
jk_printf(s, "<td>%" JK_UINT64_T_FMT "</td>", wr->s->lb_mult);
jk_printf(s, "<td>%" JK_UINT64_T_FMT "</td>", wr->s->lb_value);
jk_printf(s, "<td>%" JK_UINT64_T_FMT "</td>", wr->s->elected);
jk_printf(s, "<td>%" JK_UINT32_T_FMT "</td>", wr->s->errors);
jk_putv(s, "<td>", status_strfsize(wr->s->transferred, buf),
"</td>", NULL);
jk_putv(s, "<td>", status_strfsize(wr->s->readed, buf),
"</td>", NULL);
jk_printf(s, "<td>%u</td>", wr->s->busy);
jk_printf(s, "<td>%u</td><td>", wr->s->max_busy);
if (wr->s->redirect && *wr->s->redirect)
jk_puts(s, wr->s->redirect);
else
jk_puts(s,"&nbsp;");
jk_puts(s, "</td><td>\n");
if (wr->s->domain && *wr->s->domain)
jk_puts(s, wr->s->domain);
else
jk_puts(s,"&nbsp;");
jk_puts(s, "</td>\n</tr>\n");
}
jk_puts(s, "</table><br/>\n");
if (selected >= 0) {
worker_record_t *wr = &(lb->lb_workers[selected]);
jk_putv(s, "<hr/><h3>Edit worker settings for ",
wr->s->name, " (JVM Route ",
wr->s->jvm_route, ")</h3>\n", NULL);
jk_putv(s, "<form method=\"GET\" action=\"",
s->req_uri, "\">\n", NULL);
jk_puts(s, "<input type=\"hidden\" name=\"cmd\" ");
jk_puts(s, "value=\"update\">\n");
jk_puts(s, "<input type=\"hidden\" name=\"w\" ");
jk_putv(s, "value=\"", wr->s->name, "\">\n", NULL);
jk_puts(s, "<input type=\"hidden\" name=\"id\" ");
jk_printf(s, "value=\"%u\">\n", selected);
jk_puts(s, "<input type=\"hidden\" name=\"lb\" ");
jk_printf(s, "value=\"%u\">\n", i);
jk_puts(s, "<table>\n<tr><td>Distance:</td><td><input name=\"wx\" type=\"text\" ");
jk_printf(s, "value=\"%d\"/></td></tr>\n", wr->s->distance);
jk_puts(s, "<tr><td>Load factor:</td><td><input name=\"wf\" type=\"text\" ");
jk_printf(s, "value=\"%d\"/></td></tr>\n", wr->s->lb_factor);
jk_puts(s, "<tr><td>Route Redirect:</td><td><input name=\"wr\" type=\"text\" ");
jk_putv(s, "value=\"", wr->s->redirect, NULL);
jk_puts(s, "\"/></td></tr>\n");
jk_puts(s, "<tr><td>Cluster Domain:</td><td><input name=\"wc\" type=\"text\" ");
jk_putv(s, "value=\"", wr->s->domain, NULL);
jk_puts(s, "\"/></td></tr>\n");
jk_puts(s, "<tr><td>Disabled:</td><td><input name=\"wd\" type=\"checkbox\"");
if (wr->s->is_disabled)
jk_puts(s, " checked=\"checked\"");
jk_puts(s, "/></td></tr>\n");
jk_puts(s, "<tr><td>Stopped:</td><td><input name=\"ws\" type=\"checkbox\"");
if (wr->s->is_stopped)
jk_puts(s, " checked=\"checked\"");
jk_puts(s, "/></td></tr>\n");
jk_puts(s, "</table>\n");
jk_puts(s, "<br/><input type=\"submit\" value=\"Update Worker\"/>\n</form>\n");
}
else if (dworker && strcmp(dworker, sw->we->worker_list[i]) == 0) {
/* Edit Load balancer settings */
jk_putv(s, "<hr/><h3>Edit Load balancer settings for ",
dworker, "</h3>\n", NULL);
jk_putv(s, "<form method=\"GET\" action=\"",
s->req_uri, "\">\n", NULL);
jk_puts(s, "<input type=\"hidden\" name=\"cmd\" ");
jk_puts(s, "value=\"update\"/>\n");
jk_puts(s, "<input type=\"hidden\" name=\"w\" ");
jk_putv(s, "value=\"", dworker, "\"/>\n", NULL);
jk_puts(s, "<input type=\"hidden\" name=\"id\" ");
jk_printf(s, "value=\"%u\"/>\n", i);
jk_puts(s, "<table>\n<tr><td>Retries:</td><td><input name=\"lr\" type=\"text\" ");
jk_printf(s, "value=\"%d\"/></td></tr>\n", lb->s->retries);
jk_puts(s, "<tr><td>Recover time:</td><td><input name=\"lt\" type=\"text\" ");
jk_printf(s, "value=\"%d\"/></td></tr>\n", lb->s->recover_wait_time);
jk_puts(s, "<tr><td>Sticky session:</td><td><input name=\"ls\" type=\"checkbox\"");
if (lb->s->sticky_session)
jk_puts(s, " checked=\"checked\"");
jk_puts(s, "/></td></tr>\n");
jk_puts(s, "<tr><td>Force Sticky session:</td><td><input name=\"lf\" type=\"checkbox\"");
if (lb->s->sticky_session_force)
jk_puts(s, " checked=\"checked\"");
jk_puts(s, "/></td></tr>\n");
jk_puts(s, "</table>\n");
display_maps(s, sw, s->uw_map, dworker, l);
jk_puts(s, "<br/><input type=\"submit\" value=\"Update Balancer\"/></form>\n");
}
}
else {
jk_puts(s, "\n\n<table><tr>"
"<th>Type</th><th>Host</th><th>Addr</th>"
"</tr>\n<tr>");
jk_putv(s, "<td>", status_worker_type(w->type), "</td>", NULL);
jk_printf(s, "<td>%s:%d</td>", aw->host, aw->port);
jk_putv(s, "<td>", jk_dump_hinfo(&aw->worker_inet_addr, buf),
"</td>\n</tr>\n", NULL);
jk_puts(s, "</table>\n");
}
}
/* Display legend */
jk_puts(s, "<hr/><table>\n"
"<tr><th>Name</th><td>Worker name</td></tr>\n"
"<tr><th>Type</th><td>Worker type</td></tr>\n"
"<tr><th>jvmRoute</th><td>Worker JVM route</td></tr>\n"
"<tr><th>Addr</th><td>Backend Address info</td></tr>\n"
"<tr><th>Stat</th><td>Worker status</td></tr>\n"
"<tr><th>D</th><td>Worker distance</td></tr>\n"
"<tr><th>F</th><td>Load Balancer factor</td></tr>\n"
"<tr><th>M</th><td>Load Balancer multiplicity</td></tr>\n"
"<tr><th>V</th><td>Load Balancer value</td></tr>\n"
"<tr><th>Acc</th><td>Number of requests</td></tr>\n"
"<tr><th>Err</th><td>Number of failed requests</td></tr>\n"
"<tr><th>Wr</th><td>Number of bytes transferred/min</td></tr>\n"
"<tr><th>Rd</th><td>Number of bytes read/min</td></tr>\n"
"<tr><th>Busy</th><td>Current number of busy connections</td></tr>\n"
"<tr><th>Max</th><td>Maximum number of busy connections</td></tr>\n"
"<tr><th>RR</th><td>Route redirect</td></tr>\n"
"<tr><th>Cd</th><td>Cluster domain</td></tr>\n"
"</table>");
}
static void dump_config(jk_ws_service_t *s, status_worker_t *sw,
jk_logger_t *l)
{
unsigned int i;
char buf[32];
int has_lb = 0;
for (i = 0; i < sw->we->num_of_workers; i++) {
jk_worker_t *w = wc_get_worker_for_name(sw->we->worker_list[i], l);
if (w == NULL)
continue;
if (w->type == JK_LB_WORKER_TYPE) {
has_lb = 1;
break;
}
}
jk_printf(s, " <jk:server name=\"%s\" port=\"%d\" software=\"%s\" version=\"%s\" />\n",
s->server_name, s->server_port, s->server_software, JK_VERSTRING);
if (has_lb)
jk_puts(s, " <jk:balancers>\n");
for (i = 0; i < sw->we->num_of_workers; i++) {
jk_worker_t *w = wc_get_worker_for_name(sw->we->worker_list[i], l);
lb_worker_t *lb = NULL;
unsigned int j;
if (w == NULL)
continue;
if (w->type == JK_LB_WORKER_TYPE) {
lb = (lb_worker_t *)w->worker_private;
}
else {
/* Skip non lb workers */
continue;
}
jk_printf(s, " <jk:balancer id=\"%d\" name=\"%s\" type=\"%s\" sticky=\"%s\" stickyforce=\"%s\" retries=\"%d\" recover=\"%d\" >\n",
i,
lb->s->name,
status_worker_type(w->type),
status_val_bool(lb->s->sticky_session),
status_val_bool(lb->s->sticky_session_force),
lb->s->retries,
lb->s->recover_wait_time);
for (j = 0; j < lb->num_of_workers; j++) {
worker_record_t *wr = &(lb->lb_workers[j]);
ajp_worker_t *a = (ajp_worker_t *)wr->w->worker_private;
/* TODO: descriptive status */
jk_printf(s, " <jk:member id=\"%d\" name=\"%s\" type=\"%s\" host=\"%s\" port=\"%d\" address=\"%s\" status=\"%s\"",
j,
wr->s->name,
status_worker_type(wr->w->type),
a->host,
a->port,
jk_dump_hinfo(&a->worker_inet_addr, buf),
status_val_status(wr->s->is_stopped,
wr->s->is_disabled,
wr->s->in_error_state,
wr->s->in_recovering,
wr->s->is_busy) );
jk_printf(s, " distance=\"%d\"", wr->s->distance);
jk_printf(s, " lbfactor=\"%d\"", wr->s->lb_factor);
jk_printf(s, " lbmult=\"%" JK_UINT64_T_FMT "\"", wr->s->lb_mult);
jk_printf(s, " lbvalue=\"%" JK_UINT64_T_FMT "\"", wr->s->lb_value);
jk_printf(s, " elected=\"%" JK_UINT64_T_FMT "\"", wr->s->elected);
jk_printf(s, " errors=\"%" JK_UINT32_T_FMT "\"", wr->s->errors);
jk_printf(s, " transferred=\"%" JK_UINT64_T_FMT "\"", wr->s->transferred);
jk_printf(s, " readed=\"%" JK_UINT64_T_FMT "\"", wr->s->readed);
jk_printf(s, " busy=\"%u\"", wr->s->busy);
jk_printf(s, " maxbusy=\"%u\"", wr->s->max_busy);
if (wr->s->jvm_route && *wr->s->jvm_route)
jk_printf(s, " jvm_route=\"%s\"", wr->s->jvm_route);
if (wr->s->redirect && *wr->s->redirect)
jk_printf(s, " redirect=\"%s\"", wr->s->redirect);
if (wr->s->domain && *wr->s->domain)
jk_printf(s, " domain=\"%s\"", wr->s->domain);
jk_puts(s, " />\n");
}
dump_maps(s, sw, s->uw_map, lb->s->name, l);
jk_puts(s, " </jk:balancer>\n");
}
if (has_lb)
jk_puts(s, " </jk:balancers>\n");
}
static void update_worker(jk_ws_service_t *s, status_worker_t *sw,
const char *dworker, jk_logger_t *l)
{
int i;
int j;
char buf[1024];
const char *b;
lb_worker_t *lb;
jk_worker_t *w = wc_get_worker_for_name(dworker, l);
if (w && w->type == JK_LB_WORKER_TYPE) {
lb = (lb_worker_t *)w->worker_private;
i = status_int("lr", s->query_string, lb->s->retries);
if (i > 0)
lb->s->retries = i;
i = status_int("lt", s->query_string, lb->s->recover_wait_time);
if (i < 1)
i = 1;
lb->s->recover_wait_time = i;
lb->s->sticky_session = status_bool("ls", s->query_string);
lb->s->sticky_session_force = status_bool("lf", s->query_string);
}
else {
int n = status_int("lb", s->query_string, -1);
worker_record_t *wr = NULL;
ajp_worker_t *a;
if (n >= 0 && n < (int)sw->we->num_of_workers)
w = wc_get_worker_for_name(sw->we->worker_list[n], l);
else {
if (!(b = status_cmd("l", s->query_string, buf, sizeof(buf))))
return;
w = wc_get_worker_for_name(b, l);
}
if (!w || w->type != JK_LB_WORKER_TYPE)
return;
lb = (lb_worker_t *)w->worker_private;
i = status_int("id", s->query_string, -1);
if (i >= 0 && i < (int)lb->num_of_workers)
wr = &(lb->lb_workers[i]);
else {
for (i = 0; i < (int)lb->num_of_workers; i++) {
if (strcmp(dworker, lb->lb_workers[i].s->name) == 0) {
wr = &(lb->lb_workers[i]);
break;
}
}
}
if (!wr)
return;
a = (ajp_worker_t *)wr->w->worker_private;
if ((b = status_cmd("wr", s->query_string, buf, sizeof(buf))))
strncpy(wr->s->redirect, b, JK_SHM_STR_SIZ);
else
memset(wr->s->redirect, 0, JK_SHM_STR_SIZ);
if ((b = status_cmd("wc", s->query_string, buf, sizeof(buf))))
strncpy(wr->s->domain, b, JK_SHM_STR_SIZ);
else
memset(wr->s->domain, 0, JK_SHM_STR_SIZ);
i = status_bool("wd", s->query_string);
j = status_bool("ws", s->query_string);
if (wr->s->is_disabled!=i || wr->s->is_stopped!=j) {
wr->s->is_disabled = i;
wr->s->is_stopped = j;
reset_lb_values(lb, l);
if (i+j==0) {
jk_log(l, JK_LOG_INFO,
"worker %s restarted in status worker"
JK_UINT64_T_FMT,
wr->s->name);
}
}
i = status_int("wx", s->query_string, wr->s->distance);
if (wr->s->distance!=i) {
wr->s->distance = i;
reset_lb_values(lb, l);
}
i = status_int("wf", s->query_string, wr->s->lb_factor);
if (i > 0 && wr->s->lb_factor != i) {
wr->s->lb_factor = i;
/* Recalculate the load multiplicators wrt. lb_factor */
update_mult(lb, l);
}
}
}
static void reset_worker(jk_ws_service_t *s, status_worker_t *sw,
const char *dworker, jk_logger_t *l)
{
unsigned int i;
lb_worker_t *lb;
jk_worker_t *w = wc_get_worker_for_name(dworker, l);
if (w && w->type == JK_LB_WORKER_TYPE) {
lb = (lb_worker_t *)w->worker_private;
for (i = 0; i < lb->num_of_workers; i++) {
worker_record_t *wr = &(lb->lb_workers[i]);
wr->s->busy = 0;
wr->s->elected = 0;
wr->s->error_time = 0;
wr->s->errors = 0;
wr->s->lb_value = 0;
wr->s->max_busy = 0;
wr->s->readed = 0;
wr->s->transferred = 0;
wr->s->is_busy = JK_FALSE;
wr->s->in_error_state = JK_FALSE;
wr->s->in_recovering = JK_FALSE;
}
}
}
static int status_cmd_type(const char *req)
{
if (!req)
return 0;
else if (!strncmp(req, "cmd=list", 8))
return 0;
else if (!strncmp(req, "cmd=show", 8))
return 1;
else if (!strncmp(req, "cmd=update", 10))
return 2;
else if (!strncmp(req, "cmd=reset", 9))
return 3;
else
return 0;
}
static int status_mime_type(const char *req)
{
int ret = 0 ;
if (req) {
char mimetype[32];
if (status_cmd("mime", req, mimetype, sizeof(mimetype)) != NULL) {
if (!strcmp(mimetype, "xml"))
ret = 1;
else if (!strcmp(mimetype, "txt"))
ret = 2;
}
}
return ret ;
}
static int JK_METHOD service(jk_endpoint_t *e,
jk_ws_service_t *s,
jk_logger_t *l, int *is_recoverable_error)
{
char buf[128];
char *worker = NULL;
int cmd;
int mime;
status_endpoint_t *p;
JK_TRACE_ENTER(l);
if (is_recoverable_error)
*is_recoverable_error = JK_FALSE;
if (!e || !e->endpoint_private || !s || !is_recoverable_error) {
JK_LOG_NULL_PARAMS(l);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
p = e->endpoint_private;
/* Step 1: Process GET params and update configuration */
cmd = status_cmd_type(s->query_string);
mime = status_mime_type(s->query_string);
if (cmd > 0 && (status_cmd("w", s->query_string, buf, sizeof(buf)) != NULL))
worker = strdup(buf);
if ((cmd == 2) && worker) {
/* lock shared memory */
jk_shm_lock();
update_worker(s, p->s_worker, worker, l);
/* update modification time to reflect the current config */
jk_shm_set_workers_time(time(NULL));
/* Since we updated the config no need to reload
* on the next request
*/
jk_shm_sync_access_time();
/* unlock the shared memory */
jk_shm_unlock();
}
else if ((cmd == 3) && worker) {
/* lock shared memory */
jk_shm_lock();
reset_worker(s, p->s_worker, worker, l);
/* update modification time to reflect the current config */
jk_shm_set_workers_time(time(NULL));
/* Since we updated the config no need to reload
* on the next request
*/
jk_shm_sync_access_time();
/* unlock the shared memory */
jk_shm_unlock();
}
if (mime == 0) {
int refresh = status_int("refresh", s->query_string, -1);
s->start_response(s, 200, "OK", headers_names, headers_vhtml, 3);
s->write(s, JK_STATUS_HEAD, sizeof(JK_STATUS_HEAD) - 1);
if (cmd > 1) {
jk_putv(s, "\n<meta http-equiv=\"Refresh\" content=\"0;url=",
s->req_uri, "\">", NULL);
}
else if (cmd == 0 && refresh >= 0) {
jk_printf(s, "\n<meta http-equiv=\"Refresh\" content=\"%d;url=%s?%s\">",
refresh, s->req_uri, s->query_string);
}
if (p->s_worker->css) {
jk_putv(s, "\n<link rel=\"stylesheet\" type=\"text/css\" href=\"",
p->s_worker->css, "\" />\n", NULL);
}
s->write(s, JK_STATUS_HEND, sizeof(JK_STATUS_HEND) - 1);
if ( cmd <= 1 ) {
jk_puts(s, "<h1>JK Status Manager for ");
jk_puts(s, s->server_name);
jk_puts(s, "</h1>\n\n");
jk_putv(s, "<dl><dt>Server Version:</dt><dd>",
s->server_software, "</dd>\n", NULL);
jk_putv(s, "<dt>JK Version:</dt><dd>",
JK_VERSTRING, "\n</dd></dl>\n", NULL);
}
if ( cmd == 0 ) {
jk_putv(s, "[<a href=\"", s->req_uri, NULL);
if (refresh >= 0) {
char *buf = jk_pool_alloc(s->pool, sizeof(char *) * BIG_POOL_SIZE);
const char *str = s->query_string;
int result = 0;
int scan = 0;
while (str[scan] != 0) {
if (strncmp(&str[scan], "refresh=", 8) == 0) {
scan += 8;
while (str[scan] != 0 && str[scan] != '&')
scan++;
if (str[scan] == '&')
scan++;
}
else {
if (result > 0 && str[scan] != 0 && str[scan] != '&') {
buf[result] = '&';
result++;
}
while (str[scan] != 0 && str[scan] != '&') {
buf[result] = str[scan];
result++;
scan++;
}
if (str[scan] == '&')
scan++;
}
}
buf[result] = 0;
if (buf && buf[0])
jk_putv(s, "?", buf, NULL);
jk_puts(s, "\">stop");
}
else {
jk_puts(s, "?");
if (s->query_string && s->query_string[0])
jk_putv(s, s->query_string, "&", NULL);
jk_puts(s, "refresh=10\">start");
}
jk_puts(s, " auto update</a>]");
}
if ( cmd <= 1 ) {
/* Step 2: Display configuration */
display_workers(s, p->s_worker, worker, l);
}
s->write(s, JK_STATUS_BEND, sizeof(JK_STATUS_BEND) - 1);
}
else if (mime == 1) {
s->start_response(s, 200, "OK", headers_names, headers_vxml, 3);
s->write(s, JK_STATUS_XMLH, sizeof(JK_STATUS_XMLH) - 1);
dump_config(s, p->s_worker, l);
s->write(s, JK_STATUS_XMLE, sizeof(JK_STATUS_XMLE) - 1);
}
else {
s->start_response(s, 200, "OK", headers_names, headers_vtxt, 3);
s->write(s, JK_STATUS_TEXTUPDATE_RESPONCE,
sizeof(JK_STATUS_TEXTUPDATE_RESPONCE) - 1);
}
if (worker)
free(worker);
JK_TRACE_EXIT(l);
return JK_TRUE;
}
static int JK_METHOD done(jk_endpoint_t **e, jk_logger_t *l)
{
JK_TRACE_ENTER(l);
if (e && *e && (*e)->endpoint_private) {
*e = NULL;
JK_TRACE_EXIT(l);
return JK_TRUE;
}
JK_LOG_NULL_PARAMS(l);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
static int JK_METHOD validate(jk_worker_t *pThis,
jk_map_t *props,
jk_worker_env_t *we, jk_logger_t *l)
{
JK_TRACE_ENTER(l);
if (pThis && pThis->worker_private) {
JK_TRACE_EXIT(l);
return JK_TRUE;
}
JK_LOG_NULL_PARAMS(l);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
static int JK_METHOD init(jk_worker_t *pThis,
jk_map_t *props,
jk_worker_env_t *we, jk_logger_t *log)
{
JK_TRACE_ENTER(log);
if (pThis && pThis->worker_private) {
status_worker_t *p = pThis->worker_private;
p->we = we;
if (!jk_get_worker_str_prop(props, p->name, "css", &(p->css)))
p->css = NULL;
}
JK_TRACE_EXIT(log);
return JK_TRUE;
}
static int JK_METHOD get_endpoint(jk_worker_t *pThis,
jk_endpoint_t **pend, jk_logger_t *l)
{
JK_TRACE_ENTER(l);
if (pThis && pThis->worker_private && pend) {
status_worker_t *p = (status_worker_t *)pThis->worker_private;
*pend = p->ep.e;
JK_TRACE_EXIT(l);
return JK_TRUE;
}
else {
JK_LOG_NULL_PARAMS(l);
}
JK_TRACE_EXIT(l);
return JK_FALSE;
}
static int JK_METHOD destroy(jk_worker_t **pThis, jk_logger_t *l)
{
JK_TRACE_ENTER(l);
if (pThis && *pThis && (*pThis)->worker_private) {
status_worker_t *private_data = (*pThis)->worker_private;
jk_close_pool(&private_data->p);
free(private_data);
JK_TRACE_EXIT(l);
return JK_TRUE;
}
JK_LOG_NULL_PARAMS(l);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
int JK_METHOD status_worker_factory(jk_worker_t **w,
const char *name, jk_logger_t *l)
{
JK_TRACE_ENTER(l);
if (NULL != name && NULL != w) {
status_worker_t *private_data =
(status_worker_t *) calloc(1, sizeof(status_worker_t));
jk_open_pool(&private_data->p,
private_data->buf,
sizeof(jk_pool_atom_t) * TINY_POOL_SIZE);
private_data->name = name;
private_data->worker.worker_private = private_data;
private_data->worker.validate = validate;
private_data->worker.init = init;
private_data->worker.get_endpoint = get_endpoint;
private_data->worker.destroy = destroy;
private_data->worker.retries = 1;
/* Status worker has single static endpoint. */
private_data->ep.endpoint.done = done;
private_data->ep.endpoint.service = service;
private_data->ep.endpoint.endpoint_private = &private_data->ep;
private_data->ep.e = &(private_data->ep.endpoint);
private_data->ep.s_worker = private_data;
*w = &private_data->worker;
JK_TRACE_EXIT(l);
return JK_STATUS_WORKER_TYPE;
}
else {
JK_LOG_NULL_PARAMS(l);
}
JK_TRACE_EXIT(l);
return 0;
}