mod_heartmonitor.c revision 714a36287cb6cc92748d7e87bbe549d660acae1c
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
*
*
* 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.
*/
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "http_core.h"
#include "http_protocol.h"
#include "apr_strings.h"
#include "apr_hash.h"
#include "apr_time.h"
#include "ap_mpm.h"
#include "scoreboard.h"
#include "mod_watchdog.h"
#include "ap_slotmem.h"
#include "heartbeat.h"
#ifndef HM_UPDATE_SEC
/* How often we update the stats file */
/* TODO: Make a runtime config */
#define HM_UPDATE_SEC (5)
#endif
#define HM_WATHCHDOG_NAME ("_heartmonitor_")
static int maxworkers = 0;
typedef struct hm_server_t
{
const char *ip;
int busy;
int ready;
unsigned int port;
} hm_server_t;
typedef struct hm_ctx_t
{
int active;
const char *storage_path;
volatile int keep_running;
apr_pool_t *p;
server_rec *s;
} hm_ctx_t;
typedef struct hm_slot_server_ctx_t {
hm_server_t *s;
int found;
unsigned int item_id;
{
if (rv) {
"Heartmonitor: Failed to create listening socket.");
return rv;
}
if (rv) {
"Heartmonitor: Failed to set APR_SO_REUSEADDR to 1 on socket.");
return rv;
}
if (rv) {
"Heartmonitor: Failed to set APR_SO_NONBLOCK to 1 on socket.");
return rv;
}
if (rv) {
"Heartmonitor: Failed to bind on socket.");
return rv;
}
if (rv) {
"Heartmonitor: Failed to join multicast group");
return rv;
}
if (rv) {
"Heartmonitor: Failed to accept localhost mulitcast on socket.");
return rv;
}
return APR_SUCCESS;
}
/* XXX: The same exists in mod_lbmethod_heartbeat.c where it is named argstr_to_table */
apr_pool_t *p)
{
char *key;
char *value;
char *query_string;
char *strtok_state;
return;
}
while (key) {
if (value) {
value++; /* Skip passed the = */
}
else {
value = "1";
}
/*
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"Found query arg: %s = %s", key, value);
*/
}
}
#define SEEN_TIMEOUT (30)
/* Store in the slotmem */
{
hm_server_t *new = s->s;
s->found = 1;
}
return APR_SUCCESS;
}
/* Read the id corresponding to the entry in the slotmem */
{
hm_server_t *new = s->s;
s->found = 1;
}
return APR_SUCCESS;
}
/* update the entry or create it if not existing */
{
/* We call do_all (to try to update) otherwise grab + put */
ctx.s = s;
unsigned int i;
/* XXX locking for grab() / put() */
}
return APR_SUCCESS;
}
{
ctx.s = s;
}
return APR_SUCCESS;
}
{
apr_file_t *fp;
int updated = 0;
/* TODO: Update stats file (!) */
if (rv) {
"Heartmonitor: Unable to open tmp file: %s", path);
return rv;
}
now = apr_time_now();
if (rv == APR_SUCCESS) {
char *t;
if (rv) {
return rv;
}
/* Read the file and update the line corresponding to the node */
do {
char buf[4096];
const char *ip;
if (APR_BRIGADE_EMPTY(bb)) {
break;
}
APR_BLOCK_READ, sizeof(buf));
if (rv) {
return rv;
}
if (bsize == 0) {
break;
}
if (t) {
} else {
}
/* copy things we can't process */
/* Update seen time according to the last file modification */
} else {
}
} else {
}
} else {
}
} else {
}
} else {
updated = 1;
}
} while (1);
}
if (!updated) {
}
if (rv) {
"Heartmonitor: Unable to flush file: %s", path);
return rv;
}
if (rv) {
"Heartmonitor: Unable to close file: %s", path);
return rv;
}
"Heartmonitor: Unable to set file permssions on %s",
path);
return rv;
}
if (rv) {
"Heartmonitor: Unable to move file: %s -> %s", path,
ctx->storage_path);
return rv;
}
return APR_SUCCESS;
}
{
if (slotmem)
return hm_slotmem_update_stat(s, pool);
else
}
/* Store in a file */
{
apr_file_t *fp;
/* TODO: Update stats file (!) */
if (rv) {
"Heartmonitor: Unable to open tmp file: %s", path);
return rv;
}
now = apr_time_now();
hm_server_t *s = NULL;
if (seen > SEEN_TIMEOUT) {
/*
* Skip this entry from the heartbeat file -- when it comes back,
* we will reuse the memory...
*/
}
else {
}
}
if (rv) {
"Heartmonitor: Unable to flush file: %s", path);
return rv;
}
if (rv) {
"Heartmonitor: Unable to close file: %s", path);
return rv;
}
"Heartmonitor: Unable to set file permssions on %s",
path);
return rv;
}
if (rv) {
"Heartmonitor: Unable to move file: %s -> %s", path,
ctx->storage_path);
return rv;
}
return APR_SUCCESS;
}
/* Store in a slotmem */
{
now = apr_time_now();
hm_server_t *s = NULL;
if (seen > SEEN_TIMEOUT) {
/* remove it */
rv = hm_slotmem_remove_stat(s, p);
} else {
/* update it */
rv = hm_slotmem_update_stat(s, p);
}
if (rv !=APR_SUCCESS)
return rv;
}
return APR_SUCCESS;
}
{
if (slotmem)
return hm_slotmem_update_stats(ctx, p);
else
return hm_file_update_stats(ctx, p);
}
{
hm_server_t *s;
if (s == NULL) {
s->ready = 0;
s->busy = 0;
s->seen = 0;
}
return s;
}
/* Process a message received from a backend node */
{
char *ip;
int port = 80;
hm_server_t *s;
/* TODO: REMOVE ME BEFORE PRODUCTION (????) */
"Heartmonitor: %pI busy=%s ready=%s", from,
s->seen = apr_time_now();
}
else {
"Heartmonitor: malformed message from %pI",
from);
}
}
/* Read message from multicast socket */
#define MAX_MSG_LEN (1000)
{
if (APR_STATUS_IS_EAGAIN(rv)) {
"Heartmonitor: would block");
return APR_SUCCESS;
}
else if (rv) {
"Heartmonitor: recvfrom failed");
return rv;
}
return rv;
}
{
return rv;
}
switch (state) {
if (rv) {
"Heartmonitor: Unable to listen for connections!");
}
else {
"Heartmonitor: %s listener started.",
}
break;
/* store in the slotmem or in the file depending on configuration */
/* TODO: Insted HN_UPDATE_SEC use
* the ctx->interval
*/
int n;
apr_pool_t *p;
apr_pool_create(&p, pool);
pfd.p = p;
if (!ctx->keep_running) {
apr_pool_destroy(p);
break;
}
}
apr_pool_destroy(p);
}
break;
"Heartmonitor: stopping %s listener.",
ctx->keep_running = 0;
}
break;
}
return rv;
}
{
"Heartmonitor: mod_watchdog is required");
return !OK;
}
/* Create the slotmem */
/* this is the real thing */
if (maxworkers) {
if (!storage) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, 0, s, "ap_lookup_provider %s failed", AP_SLOTMEM_PROVIDER_GROUP);
return !OK;
}
storage->create(&slotmem, "mod_heartmonitor", sizeof(hm_slot_server_t), maxworkers, AP_SLOTMEM_TYPE_PREGRAB, p);
if (!slotmem) {
return !OK;
}
}
}
return OK;
}
0, 1, p);
if (rv) {
"Heartmonitor: Failed to create watchdog "
"instance (%s)", HM_WATHCHDOG_NAME);
return !OK;
}
/* Register a callback with zero interval. */
0,
ctx,
if (rv) {
"Heartmonitor: Failed to register watchdog "
"callback (%s)", HM_WATHCHDOG_NAME);
return !OK;
}
"Heartmonitor: wd callback %s", HM_WATHCHDOG_NAME);
return OK;
}
static int hm_handler(request_rec *r)
{
char *buf;
char *ip;
return DECLINED;
}
if (r->method_number != M_POST) {
return HTTP_METHOD_NOT_ALLOWED;
}
len = MAX_MSG_LEN;
status = ap_get_brigade(r->input_filters, input_brigade, AP_MODE_READBYTES, APR_BLOCK_READ, MAX_MSG_LEN);
if (status != APR_SUCCESS) {
return HTTP_INTERNAL_SERVER_ERROR;
}
/* we can't use hm_processmsg because it uses hm_get_server() */
ap_set_content_type(r, "text/plain");
ap_set_content_length(r, 2);
ap_rputs("OK", r);
ap_rflush(r);
return OK;
}
static void hm_register_hooks(apr_pool_t *p)
{
}
{
/* TODO: Add directive for tuning the update interval
*/
ctx->s = s;
apr_pool_create(&ctx->p, p);
return ctx;
}
{
return err;
}
return NULL;
}
void *dconf, const char *mcast_addr)
{
char *host_str;
char *scope_id;
apr_port_t port = 0;
return err;
}
}
else {
return "HeartbeatListen: May only be specified once.";
}
if (rv) {
return "HeartbeatListen: Unable to parse multicast address.";
}
return "HeartbeatListen: No host provided in multicast address";
}
if (port == 0) {
return "HeartbeatListen: No port provided in multicast address";
}
p);
if (rv) {
return
"HeartbeatListen: apr_sockaddr_info_get failed on multicast address";
}
return NULL;
}
{
return err;
}
if (maxworkers <= 10)
return "HeartbeatMaxServers: Should be bigger than 10";
return NULL;
}
static const command_rec hm_cmds[] = {
"Address to listen for heartbeat requests"),
"Path to store heartbeat data."),
"Max number of servers when using slotmem (instead file) to store heartbeat data."),
{NULL}
};
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
hm_create_config, /* create per-server config structure */
NULL, /* merge per-server config structures */
hm_cmds, /* command apr_table_t */
};