Download | Plain Text | Line Numbers
/*
* Copyright (C) 2000-2002 David Jao <djao@dominia.org>
* "MaxConnPerUid", "MaxConnPerVhost" and "MaxLA*" portions by Maxim Chirkov <mc@tyumen.ru>
* per VHost settings, advanced content-type shm-cache and check, scoreboard dump,
* optional KeepAlive-Values, code reorganization and apache2 port by Manuel Mausz <manuel@mausz.at>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, this permission notice, and the
* following disclaimer shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
#define CORE_PRIVATE
#include "httpd.h"
#include "http_request.h"
#include "http_core.h"
#include "http_log.h"
#include "scoreboard.h"
#include "unixd.h"
#include "ap_mpm.h"
#include "apr_strings.h"
#if HAVE_SHMGET || APR_HAS_SHARED_MEMORY
# define CREATE_OWN_SCOREBOARD
# include <sys/ipc.h>
# include <sys/shm.h>
#endif
#define VERSION "0.22-vhost"
#define SHM_KEY IPC_PRIVATE
//#define DEBUG
//#define DUMP
#ifdef DUMP
# include <sys/types.h>
# include <unistd.h>
# include <time.h>
# ifndef DEFAULT_TIME_FORMAT
# define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
# endif
#endif
#define LIPC_LOG(level,errno) level,APR_FROM_OS_ERROR(errno)
AP_DECLARE_MODULE(limitipconn);
static int server_limit, thread_limit;
#ifdef CREATE_OWN_SCOREBOARD
/* apache2 doesn't export ap_sb_handle_t via scoreboard.h */
struct ap_sb_handle_t
{
int child_num;
int thread_num;
};
/* our own personal scoreboard */
typedef struct
{
uid_t uid;
char handler[100];
} limit_scoreboard_t;
static void *score_board = (void *)-1;
#endif
typedef struct
{
int limit; /* max number of connections per IP */
int limit_ka; /* optional keepalive limit */
int limit_vhost; /* max number of connections per virtual host */
int limit_vhost_ka; /* optional keepalive limit */
double limit_la1; /* maximum value of Load Average for 1 min. */
double limit_la5; /* maximum value of Load Average for 5 min. */
double limit_la15; /* maximum value of Load Average for 15 min. */
#ifdef CREATE_OWN_SCOREBOARD
int limit_uid; /* max number of connections per user */
int limit_uid_ka; /* optional keepalive limit */
apr_array_header_t *excl_limit; /* array of MIME types to limit check; all
other types are exempt */
#endif
} limitipconn_config_t;
#ifdef DUMP
static const char *ap_scdump_fname = NULL;
static int dump_scoreboard(request_rec *r, const char *reason,
int matches[server_limit][thread_limit])
{
char buf[255];
FILE *fd;
time_t nowtime = time(NULL);
int i, j;
char *vhost = NULL;
pid_t worker_pid;
if (!ap_scdump_fname)
{
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r,
"Error: Trying to dump scoreboard, but directive ScoreboardDump isn't set");
return 1;
}
apr_snprintf(buf, sizeof(buf), "%s.%u", ap_scdump_fname, getpid());
char *fname = ap_server_root_relative(r->pool, buf);
if (!(fd = fopen(fname, "a")))
{
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, errno), r,
"Error while opening: %s.", fname);
return 1;
}
ap_generation_t mpm_generation;
ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
fprintf(fd, "Scoreboard Dump for %s due to %s restriction\n", ap_get_server_name(r), reason);
fprintf(fd, "Server Version: %s\n", ap_get_server_description());
fprintf(fd, "Current Time: %s\n", ap_ht_time(r->pool, nowtime, DEFAULT_TIME_FORMAT, 0));
fprintf(fd, "Request URI: %s [%s]\n", ap_escape_logitem(r->pool, r->unparsed_uri),
ap_escape_logitem(r->pool, r->hostname));
fprintf(fd, "Parent Server Generation: %d\n", mpm_generation);
fputs("\n", fd);
fflush(fd);
fputs("Server Details:\n", fd);
for (i = 0; i < server_limit; i++)
{
for (j = 0; j < thread_limit; j++)
{
worker_score *ws_record = ap_get_scoreboard_worker_from_indexes(i, j);
process_score *ps_record = ap_get_scoreboard_process(i);
if (ps_record->generation == mpm_generation)
vhost = ws_record->vhost;
switch (ws_record->status)
{
case SERVER_BUSY_READ:
case SERVER_BUSY_WRITE:
case SERVER_BUSY_KEEPALIVE:
case SERVER_BUSY_LOG:
case SERVER_BUSY_DNS:
case SERVER_GRACEFUL:
if (!matches[i][j])
continue;
break;
default:
continue;
}
/* MPM sets per-worker pid and generation */
ap_generation_t worker_generation;
if (ws_record->pid)
{
worker_pid = ws_record->pid;
worker_generation = ws_record->generation;
}
else
{
worker_pid = ps_record->pid;
worker_generation = ps_record->generation;
}
if (ws_record->status == SERVER_DEAD)
fprintf(fd, "Server %d-%d (-): [", i, worker_generation);
else
fprintf(fd, "Server %d-%d (%d): [", i, worker_generation, worker_pid);
switch (ws_record->status)
{
case SERVER_READY:
fputs("Ready", fd);
break;
case SERVER_STARTING:
fputs("Starting", fd);
break;
case SERVER_BUSY_READ:
fputs("Read", fd);
break;
case SERVER_BUSY_WRITE:
fputs("Write", fd);
break;
case SERVER_BUSY_KEEPALIVE:
fputs("Keepalive", fd);
break;
case SERVER_BUSY_LOG:
fputs("Logging", fd);
break;
case SERVER_BUSY_DNS:
fputs("DNS lookup", fd);
break;
case SERVER_DEAD:
fputs("Dead", fd);
break;
case SERVER_GRACEFUL:
fputs("Graceful", fd);
break;
default:
fputs("?STATE?", fd);
break;
}
uid_t userid = 0;
char *handler = "(unavailable)";
#ifdef CREATE_OWN_SCOREBOARD
limit_scoreboard_t (* sb)[thread_limit] = score_board;
userid = sb[i][j].uid;
handler = sb[i][j].handler;
#endif
fprintf(fd, "] %s {%s %s} [%s (%d)]\n",
ap_escape_html(r->pool, ws_record->client),
ap_escape_html(r->pool, ap_escape_logitem(r->pool, ws_record->request)),
handler, vhost ? ap_escape_html(r->pool, vhost) : "(unavailable)",
userid);
}
}
fputs("\n-------------------------------------------------------------------------------\n\n", fd);
fclose(fd);
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r,
"Wrote scoreboard dump to: %s", fname);
return 0;
}
#endif
static int limitipconn_handler(request_rec *r)
{
limitipconn_config_t *cfg = ap_get_module_config(r->server->module_config,
&limitipconn_module);
int requests_ip = 0, requests_vhost = 0;
const char *current_vhost = NULL, *current_ip = NULL;
int i, j;
#ifdef CREATE_OWN_SCOREBOARD
char **exlim = (char **)cfg->excl_limit->elts;
int requests_uid = 0;
uid_t current_uid = 0;
#endif
#ifdef DUMP
int matches[server_limit][thread_limit];
#endif
/* a limit value of 0 by convention means no limit. */
if (cfg->limit <= 0 && cfg->limit_ka <= 0
#ifdef CREATE_OWN_SCOREBOARD
&& cfg->limit_uid <= 0 && cfg->limit_uid_ka <= 0
#endif
&& cfg->limit_vhost <= 0 && cfg->limit_vhost_ka <= 0
&& cfg->limit_la1 <= 0.0 && cfg->limit_la5 <= 0.0 && cfg->limit_la15 <= 0.0)
{
#ifdef DEBUG
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r,
"OK: No limit");
#endif
return OK;
}
#ifdef CREATE_OWN_SCOREBOARD
ap_sb_handle_t *sbh = r->connection->sbh;
if (sbh == NULL)
{
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r,
"Warning: Request has no reference to scoreboard!");
return OK;
}
limit_scoreboard_t (* sb)[thread_limit] = score_board;
limit_scoreboard_t *sbrec = &sb[sbh->child_num][sbh->thread_num];
/* cycle through the exclusive list, if it exists; if our handler
* is not present, bail out */
if (cfg->excl_limit->nelts > 0)
{
/* always update our scoreboard */
if (r->handler)
apr_cpystrn(sbrec->handler, r->handler, sizeof(sbrec->handler));
else
sbrec->handler[0] = 0;
#ifdef DEBUG
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r,
"uri: %s handler: %s", r->uri, r->handler);
#endif
for (i = 0; r->handler && i < cfg->excl_limit->nelts; i++)
{
if (strncmp(exlim[i], r->handler, strlen(exlim[i])) == 0)
break;
}
if (i == cfg->excl_limit->nelts)
{
#ifdef DEBUG
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r,
"OK: %s not checked", r->handler);
#endif
return OK;
}
}
#endif /* CREATE_OWN_SCOREBOARD */
/* check load average overflow */
if (cfg->limit_la1 > 0.0 || cfg->limit_la5 > 0.0 || cfg->limit_la15 > 0.0)
{
double current_la[3];
if (getloadavg(current_la, 3) != -1)
{
if ((current_la[0] >= cfg->limit_la1) && (current_la[1] >= cfg->limit_la5)
&& (current_la[2] >= cfg->limit_la15))
{
r->connection->keepalive = -1;
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r,
"Load Average limit exceeded (limit: %.2f, %.2f, %.2f)",
cfg->limit_la1, cfg->limit_la5, cfg->limit_la15);
return HTTP_SERVICE_UNAVAILABLE;
}
}
}
/* get client ip */
if (cfg->limit > 0 || cfg->limit_ka > 0)
current_ip = r->connection->client_ip;
/* get uid of current virual host for future use */
if (r->server->is_virtual)
{
#ifdef CREATE_OWN_SCOREBOARD
if (cfg->limit_uid > 0 || cfg->limit_uid_ka > 0)
{
ap_unix_identity_t *identity = ap_run_get_suexec_identity(r);
if (identity != NULL)
current_uid = identity->uid;
}
sbrec->uid = current_uid;
#endif
if (cfg->limit_vhost > 0 || cfg->limit_vhost_ka > 0)
current_vhost = r->server->server_hostname;
}
ap_generation_t mpm_generation;
ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
/* count up the number of connections we are handling right now from
* this IP address */
for (i = 0; i < server_limit; i++)
{
for (j = 0; j < thread_limit; j++)
{
#ifdef DUMP
matches[i][j] = 0;
#endif
worker_score *ws_record = ap_get_scoreboard_worker_from_indexes(i, j);
process_score *ps_record = ap_get_scoreboard_process(i);
switch (ws_record->status)
{
case SERVER_BUSY_READ:
case SERVER_BUSY_WRITE:
case SERVER_BUSY_KEEPALIVE:
case SERVER_BUSY_LOG:
case SERVER_BUSY_DNS:
case SERVER_GRACEFUL:
#ifdef CREATE_OWN_SCOREBOARD
/* cycle through the exclusive list, if it exists; if our handler
* is not present, skip */
if (cfg->excl_limit->nelts > 0)
{
/* skip if no handler is set */
if (!sb[i][j].handler)
continue;
int k;
for (k = 0; k < cfg->excl_limit->nelts; k++)
{
if (strncmp(exlim[k], sb[i][j].handler, strlen(exlim[k])) == 0)
break;
}
if (k == cfg->excl_limit->nelts)
continue;
}
#endif
if (current_ip && strncmp(current_ip, ws_record->client,
sizeof(ws_record->client)) == 0)
{
requests_ip++;
#ifdef DUMP
matches[i][j] = 1;
#endif
}
if (ps_record->generation == mpm_generation)
{
#ifdef CREATE_OWN_SCOREBOARD
if (current_uid && current_uid != ap_unixd_config.user_id
&& sb[i][j].uid == current_uid)
requests_uid++;
#endif
if (current_vhost && (strncmp(ws_record->vhost, current_vhost,
strlen(current_vhost)) == 0))
{
requests_vhost++;
}
}
break;
default:
break;
}
}
}
/* turn off keepalive if keepalive-limit reached */
if ((cfg->limit_ka > 0 && requests_ip > cfg->limit_ka)
#ifdef CREATE_OWN_SCOREBOARD
|| (cfg->limit_uid_ka > 0 && requests_uid > cfg->limit_uid_ka)
#endif
|| (cfg->limit_vhost_ka > 0 && requests_vhost > cfg->limit_vhost_ka))
{
#ifdef DEBUG
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r,
"Keepalive-limit reached. Disabling keepalive.");
#endif
r->connection->keepalive = -1;
}
/* hard limit reached */
if (cfg->limit > 0 && requests_ip > cfg->limit)
{
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r,
"Rejected, too many connections from address %s (limit: %d).",
current_ip, cfg->limit);
apr_table_setn(r->subprocess_env, "LIMITIP", "1");
#ifdef DUMP
dump_scoreboard(r, "IP-Limit", matches);
#endif
return HTTP_SERVICE_UNAVAILABLE;
}
#ifdef CREATE_OWN_SCOREBOARD
else if (cfg->limit_uid > 0 && requests_uid > cfg->limit_uid)
{
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r,
"Rejected, too many connections for uid %d (limit: %d)",
current_uid, cfg->limit_uid);
#ifdef DUMP
dump_scoreboard(r, "UID-Limit", matches);
#endif
return HTTP_SERVICE_UNAVAILABLE;
}
#endif
else if (cfg->limit_vhost > 0 && requests_vhost > cfg->limit_vhost)
{
ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r,
"Rejected, too many connections for vhost %s (limit: %d)",
current_vhost, cfg->limit_vhost);
#ifdef DUMP
dump_scoreboard(r, "Vhost-Limit", matches);
#endif
return HTTP_SERVICE_UNAVAILABLE;
}
return OK;
}
static void *limitipconn_create_config(apr_pool_t *p, server_rec *s)
{
limitipconn_config_t *cfg = apr_pcalloc(p, sizeof(*cfg));
/* default configuration: no limit, and both arrays are empty */
cfg->limit = -1;
cfg->limit_ka = -1;
cfg->limit_vhost = -1;
cfg->limit_vhost_ka = -1;
cfg->limit_la1 = -1.0;
cfg->limit_la5 = -1.0;
cfg->limit_la15 = -1.0;
#ifdef CREATE_OWN_SCOREBOARD
cfg->limit_uid = -1;
cfg->limit_uid_ka = -1;
cfg->excl_limit = apr_array_make(p, 0, sizeof(char *));
#endif
return cfg;
}
/* simple merge: per vhost entries overrides main server entries */
static void *limitipconn_merge_config(apr_pool_t *p, void *BASE, void *ADD)
{
limitipconn_config_t *base = BASE;
limitipconn_config_t *add = ADD;
limitipconn_config_t *cfg = apr_pcalloc(p, sizeof(*cfg));
cfg->limit = (add->limit == -1) ? base->limit : add->limit;
cfg->limit_ka = (add->limit_ka == -1) ? base->limit_ka : add->limit_ka;
#ifdef CREATE_OWN_SCOREBOARD
cfg->limit_uid = (add->limit_uid == -1) ? base->limit_uid : add->limit_uid;
cfg->limit_uid_ka = (add->limit_uid_ka == -1) ? base->limit_uid_ka : add->limit_uid_ka;
#endif
cfg->limit_vhost = (add->limit_vhost == -1) ? base->limit_vhost : add->limit_vhost;
cfg->limit_vhost_ka = (add->limit_vhost_ka == -1) ? base->limit_vhost_ka : add->limit_vhost_ka;
cfg->limit_la1 = (add->limit_la1 == -1.0) ? base->limit_la1 : add->limit_la1;
cfg->limit_la5 = (add->limit_la5 == -1.0) ? base->limit_la5 : add->limit_la5;
cfg->limit_la15 = (add->limit_la15 == -1.0) ? base->limit_la15 : add->limit_la15;
#ifdef CREATE_OWN_SCOREBOARD
cfg->excl_limit = (add->excl_limit->nelts == 0) ? base->excl_limit : add->excl_limit;
#endif
return cfg;
}
/* parse the MaxConnPerIP directive */
static const char *limit_config_cmd(cmd_parms *cmd, void *mconfig,
const char *arg1, const char *arg2)
{
limitipconn_config_t *cfg = ap_get_module_config(cmd->server->module_config,
&limitipconn_module);
long limit = strtol(arg1, (char **)NULL, 10);
if (limit < 0 || limit > INT_MAX)
return "Integer overflow or invalid number";
cfg->limit = limit;
if (arg2)
{
long limit_ka = strtol(arg2, (char **)NULL, 10);
if (limit_ka < 0 || limit_ka > INT_MAX)
return "Integer overflow or invalid number";
if (limit > 0 && limit_ka > limit)
return "KeepAlive-limit has to be smaller than hard limit.";
cfg->limit_ka = limit_ka;
}
return NULL;
}
#ifdef CREATE_OWN_SCOREBOARD
/* parse the IPLimit directive */
static const char *excl_limit_config_cmd(cmd_parms *cmd, void *mconfig,
const char *arg)
{
limitipconn_config_t *cfg = ap_get_module_config(cmd->server->module_config,
&limitipconn_module);
*(char **)apr_array_push(cfg->excl_limit) = apr_pstrdup(cmd->pool, arg);
return NULL;
}
#endif
/* parse the MaxConnPerVhost directive */
static const char *limit_vhost_config_cmd(cmd_parms *cmd, void *mconfig,
const char *arg1, const char *arg2)
{
limitipconn_config_t *cfg = ap_get_module_config(cmd->server->module_config,
&limitipconn_module);
long limit = strtol(arg1, (char **)NULL, 10);
if (limit < 0 || limit > INT_MAX)
return "Integer overflow or invalid number";
cfg->limit_vhost = limit;
if (arg2)
{
long limit_ka = strtol(arg2, (char **)NULL, 10);
if (limit_ka < 0 || limit_ka > INT_MAX)
return "Integer overflow or invalid number";
if (limit > 0 && limit_ka > limit)
return "KeepAlive-limit has to be smaller than hard limit.";
cfg->limit_vhost_ka = limit_ka;
}
return NULL;
}
#ifdef CREATE_OWN_SCOREBOARD
/* parse the MaxConnPerUid directive */
static const char *limit_uid_config_cmd(cmd_parms *cmd, void *mconfig,
const char *arg1, const char *arg2)
{
limitipconn_config_t *cfg = ap_get_module_config(cmd->server->module_config,
&limitipconn_module);
long limit = strtol(arg1, (char **)NULL, 10);
if (limit < 0 || limit > INT_MAX)
return "Integer overflow or invalid number";
cfg->limit_uid = limit;
if (arg2)
{
long limit_ka = strtol(arg2, (char **)NULL, 10);
if (limit_ka < 0 || limit_ka > INT_MAX)
return "Integer overflow or invalid number";
if (limit > 0 && limit_ka > limit)
return "KeepAlive-limit has to be smaller than hard limit.";
cfg->limit_uid_ka = limit_ka;
}
return NULL;
}
#endif
/* parse the MaxLA1 directive */
static const char *limit_la1_config_cmd(cmd_parms *cmd, void *mconfig,
const char *arg)
{
limitipconn_config_t *cfg = ap_get_module_config(cmd->server->module_config,
&limitipconn_module);
double limit = strtod(arg, (char **)NULL);
if (limit < 0.0)
return "Invalid LA1 value";
cfg->limit_la1 = limit;
return NULL;
}
/* parse the MaxLA5 directive */
static const char *limit_la5_config_cmd(cmd_parms *cmd, void *mconfig,
const char *arg)
{
limitipconn_config_t *cfg = ap_get_module_config(cmd->server->module_config,
&limitipconn_module);
double limit = strtod(arg, (char **)NULL);
if (limit < 0.0)
return "Invalid LA5 value";
cfg->limit_la5 = limit;
return NULL;
}
/* parse the MaxLA15 directive */
static const char *limit_la15_config_cmd(cmd_parms *cmd, void *mconfig,
const char *arg)
{
limitipconn_config_t *cfg = ap_get_module_config(cmd->server->module_config,
&limitipconn_module);
double limit = strtod(arg, (char **)NULL);
if (limit < 0.0)
return "Invalid LA15 value";
cfg->limit_la15 = limit;
return NULL;
}
#ifdef DUMP
static const char *set_scoreboard_dump(cmd_parms *cmd, void *mconfig,
const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL)
return err;
if (cmd->server->is_virtual)
return "ScoreboardDump directive not allowed in <VirtualHost>";
ap_scdump_fname = arg;
return NULL;
}
#endif
static command_rec limitipconn_cmds[] = {
AP_INIT_TAKE12("MaxConnPerIP", limit_config_cmd, NULL, RSRC_CONF,
"maximum simultaneous connections per IP address. Second value is optional."
" If reached, KeepAlive will be turned off."),
#ifdef CREATE_OWN_SCOREBOARD
AP_INIT_ITERATE("IPLimit", excl_limit_config_cmd, NULL, RSRC_CONF,
"restrict limit checking to these handlers/MIME types only."),
#endif
#ifdef CREATE_OWN_SCOREBOARD
AP_INIT_TAKE12("MaxConnPerUid", limit_uid_config_cmd, NULL, RSRC_CONF,
"maximum simultaneous connections per user. Second value is optional."
" If reached, KeepAlive will be turned off."),
#endif
AP_INIT_TAKE12("MaxConnPerVhost", limit_vhost_config_cmd, NULL, RSRC_CONF,
"maximum simultaneous connections per virtual host. Second value is optional."
" If reached, KeepAlive will be turned off."),
AP_INIT_TAKE1("MaxLA1", limit_la1_config_cmd, NULL, RSRC_CONF,
"maximum Load Overage value for the past 1 minute."),
AP_INIT_TAKE1("MaxLA5", limit_la5_config_cmd, NULL, RSRC_CONF,
"maximum Load Overage value for the past 5 minutes."),
AP_INIT_TAKE1("MaxLA15", limit_la15_config_cmd, NULL, RSRC_CONF,
"maximum Load Overage value for the past 15 minutes."),
#ifdef DUMP
AP_INIT_TAKE1("ScoreboardDump", set_scoreboard_dump, NULL, RSRC_CONF,
"The filename of the scoreboard dumps. Will be appended by the process id."),
#endif
{ NULL },
};
#ifdef CREATE_OWN_SCOREBOARD
static apr_status_t limit_detach_shm(void *data)
{
shmdt(data);
return OK;
}
#endif
static int limitipconn_init(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
/* initialize_module() will be called twice, and if it's a DSO
* then all static data from the first call will be lost. Only
* set up our static data on the second call. */
if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG)
return OK;
ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
#ifdef CREATE_OWN_SCOREBOARD
int shmid;
if ((shmid = shmget(SHM_KEY, sizeof(limit_scoreboard_t) * server_limit * thread_limit,
IPC_CREAT | SHM_R | SHM_W)) == -1)
{
ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_EMERG, errno), s,
"Could not initialize shared memory");
exit(1);
}
#ifdef DEBUG
ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), s,
"Initialized shared memory.");
#endif
if ((score_board = shmat(shmid, NULL, 0)) == (void *)-1)
ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_EMERG, errno), s, "shmat error");
/* exit later after marking the segment to remove itself */
else
apr_pool_cleanup_register(p, (void *)score_board, limit_detach_shm,
apr_pool_cleanup_null);
/* mark the segment for deletion once all attachments are detached */
if (shmctl(shmid, IPC_RMID, NULL) == -1)
ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_EMERG, errno), s,
"Could not mark shared segment for deletion, you must manually clean it up");
/* exit if we didn't attach successfully */
if (score_board == (void *)-1)
exit(1);
#endif
ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_NOTICE, 0), s, "Initialized");
return OK;
}
static void register_hooks(apr_pool_t * p)
{
ap_hook_post_config(limitipconn_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_fixups(limitipconn_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
module AP_MODULE_DECLARE_DATA limitipconn_module =
{
STANDARD20_MODULE_STUFF,
NULL, /* per-directory config creator */
NULL, /* dir config merger */
limitipconn_create_config, /* server config creator */
limitipconn_merge_config, /* server config merger */
limitipconn_cmds, /* command table */
register_hooks, /* set up other request processing hooks */
};