/* * Copyright (C) 2000-2002 David Jao * "MaxConnPerUid", "MaxConnPerVhost" and "MaxLA*" portions by Maxim Chirkov * per VHost settings, advanced content-type shm-cache and check, scoreboard dump, * optional KeepAlive-Values, code reorganization and apache2 port by Manuel Mausz * * 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 # include #endif #define VERSION "0.22-vhost" #define SHM_KEY IPC_PRIVATE //#define DEBUG //#define DUMP #ifdef DUMP # include # include # include # 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 "; 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 */ };