/* * 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_config.h" #include "http_request.h" #include "http_protocol.h" #include "http_core.h" #include "http_main.h" #include "http_log.h" #include "scoreboard.h" #include /* apache2 specific includes */ #include "unixd.h" #include "ap_mpm.h" #include "apr_strings.h" #if HAVE_SHMGET || APR_HAS_SHARED_MEMORY # define CREATE_OWN_SCOREBOARD 1 # include # include #else # define CREATE_OWN_SCOREBOARD 0 #endif #define MODULE_NAME "mod_limitipconn" #define MODULE_VERSION "0.21-vhost" #define SHM_KEY IPC_PRIVATE //#define DEBUG 1 //#define DUMP 1 #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) module AP_MODULE_DECLARE_DATA limitipconn_module; static int server_limit, thread_limit; #if 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; }; #endif #if CREATE_OWN_SCOREBOARD /* our own personal scoreboard */ typedef struct { uid_t userid; char handler[100]; } limit_scoreboard; //static limit_scoreboard *sb = (limit_scoreboard *)-1; static void *sb = (void *)-1; static int limit_shmid; #endif typedef struct { int limit; /* max number of connections per IP */ int limit_ka; /* optional keepalive limit */ #if CREATE_OWN_SCOREBOARD int limit_uid; /* max number of connections per user */ int limit_uid_ka; /* optional keepalive limit */ #endif 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. */ #if CREATE_OWN_SCOREBOARD apr_array_header_t *excl_limit; /* array of MIME types to limit check; all other types are exempt */ #endif } limitipconn_config; static void *limitipconn_create_config(apr_pool_t *p, server_rec *s) { limitipconn_config *cfg = (limitipconn_config *)apr_pcalloc(p, sizeof(*cfg)); /* default configuration: no limit, and both arrays are empty */ cfg->limit = -1; cfg->limit_ka = -1; #if CREATE_OWN_SCOREBOARD cfg->limit_uid = -1; cfg->limit_uid_ka = -1; #endif cfg->limit_vhost = -1; cfg->limit_vhost_ka = -1; cfg->limit_la1 = -1.0; cfg->limit_la5 = -1.0; cfg->limit_la15 = -1.0; #if CREATE_OWN_SCOREBOARD cfg->excl_limit = apr_array_make(p, 0, sizeof(char *)); #endif return cfg; } #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]) { worker_score *ws_record; process_score *ps_record; char buf[255]; char *fname; FILE *fd; time_t nowtime = time(NULL); int i, j, dump; char *vhost = NULL; pid_t worker_pid; ap_generation_t worker_generation; uid_t userid = 0; #if CREATE_OWN_SCOREBOARD limit_scoreboard (* sb2)[thread_limit]; sb2 = (limit_scoreboard (*)[thread_limit])sb; #endif 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; } sprintf(buf, "%s.%ld", ap_scdump_fname, (long)getpid()); 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; } 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_version()); 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", (int)ap_my_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++) { ws_record = ap_get_scoreboard_worker(i, j); ps_record = ap_get_scoreboard_process(i); if (ps_record->generation == ap_my_generation) vhost = ws_record->vhost; dump = 0; 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]) dump = 1; break; default: break; } if (!dump) continue; /* MPM sets per-worker pid and 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) #ifdef TPF if (kill(ps_record->pid, 0) == 0) { /* on TPF show PIDs of the living dead */ fprintf(fd, "Server %d-%d (%d): [", i, worker_generation, worker_pid); } else #endif 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; } #if CREATE_OWN_SCOREBOARD userid = sb2[i][j].userid; #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)), #if CREATE_OWN_SCOREBOARD sb2[i][j].handler, #else "(unavailable)", #endif 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 /* Simple merge: Per vhost entries overrides main server entries */ static void *limitipconn_merge_config(apr_pool_t *p, void *BASE, void *ADD) { limitipconn_config *base = BASE; limitipconn_config *add = ADD; limitipconn_config *cfg = (limitipconn_config *)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; #if 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; #if CREATE_OWN_SCOREBOARD cfg->excl_limit = (add->excl_limit->nelts == 0) ? base->excl_limit : add->excl_limit; #endif return cfg; } static int limitipconn_handler(request_rec *r) { limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(r->server->module_config, &limitipconn_module); #if CREATE_OWN_SCOREBOARD limit_scoreboard (* sb2)[thread_limit]; limit_scoreboard *sbrec = NULL; char **exlim = (char **)cfg->excl_limit->elts; int found; ap_unix_identity_t *identity; ap_sb_handle_t *sbh; #endif double current_la[3]; int ip_req_count = 0; const char *address = NULL; #if CREATE_OWN_SCOREBOARD int uid_req_count = 0; uid_t current_uid = 0; #endif int vhost_req_count = 0; const char *current_vhost = NULL; worker_score *ws_record; process_score *ps_record; int i, j, k; #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 #if 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, "mod_limitipconn: OK: No limit"); #endif return OK; } #if CREATE_OWN_SCOREBOARD sb2 = (limit_scoreboard (*)[thread_limit])sb; sbh = r->connection->sbh; if (sbh == NULL) { ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r, "mod_limitipconn: Warning: Request has no reference to scoreboard!"); return OK; } sbrec = &sb2[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) - 1); else sbrec->handler[0] = 0; # ifdef DEBUG ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r, "mod_limitipconn: uri: %s handler: %s", r->uri, r->handler); # endif found = 0; for (i = 0; r->handler && i < cfg->excl_limit->nelts && !found; i++) { if (strncmp(exlim[i], r->handler, strlen(exlim[i])) == 0) found = 1; } if (!found) { # ifdef DEBUG ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r, "mod_limitipconn: 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) { 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 address */ if (cfg->limit > 0 || cfg->limit_ka > 0) { #ifdef RECORD_FORWARD if ((address = ap_table_get(r->headers_in, "X-Forwarded-For")) == NULL) #endif address = r->connection->remote_ip; } /* Get uid of current virual host for future use */ if (r->server->is_virtual) { #if CREATE_OWN_SCOREBOARD if (cfg->limit_uid > 0 || cfg->limit_uid_ka > 0) { if ((identity = ap_run_get_suexec_identity(r))) current_uid = identity->uid; } sbrec->userid = current_uid; #endif if (cfg->limit_vhost > 0 || cfg->limit_vhost_ka > 0) current_vhost = r->server->server_hostname; } /* Count up the number of connections we are handling right now from * this IP address */ for (j = 0; j < server_limit; j++) { for (k = 0; k < thread_limit; k++) { #ifdef DUMP matches[j][k] = 0; #endif ws_record = ap_get_scoreboard_worker(j, k); 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: #if 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 (!sb2[j][k].handler) continue; found = 0; for (i = 0; i < cfg->excl_limit->nelts && !found; i++) { if (strncmp(exlim[i], sb2[j][k].handler, strlen(exlim[i])) == 0) found = 1; } if (!found) continue; } #endif if (address && (strcmp(address, ws_record->client) == 0) #ifdef RECORD_FORWARD || (strcmp(address, ws_record->fwdclient) == 0) #endif ) { ip_req_count++; #ifdef DUMP matches[j][k] = 1; #endif } #if CREATE_OWN_SCOREBOARD if ((ps_record->generation == ap_my_generation)) { if (current_uid && current_uid != unixd_config.user_id && sb2[j][k].userid == current_uid) uid_req_count++; if (current_vhost && (strcmp(ws_record->vhost, current_vhost) == 0)) vhost_req_count++; } #endif break; default: break; } } } /* turn off keepalive if keepalive-limit reached */ if ((cfg->limit_ka > 0 && ip_req_count > cfg->limit_ka) #if CREATE_OWN_SCOREBOARD || (cfg->limit_uid_ka > 0 && uid_req_count > cfg->limit_uid_ka) #endif || (cfg->limit_vhost_ka > 0 && vhost_req_count > cfg->limit_vhost_ka)) { #ifdef DEBUG ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r, "mod_limitipconn: keepalive-limit reached. deactivating keepalive."); #endif r->connection->keepalive = -1; } /* hard limit reached */ if (cfg->limit > 0 && ip_req_count > cfg->limit) { ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r, "Rejected, too many connections from address %s (limit: %d).", address, cfg->limit); apr_table_setn(r->subprocess_env, "LIMITIP", "1"); #ifdef DUMP dump_scoreboard(r, "IP-Limit", matches); #endif return HTTP_SERVICE_UNAVAILABLE; } #if CREATE_OWN_SCOREBOARD else if (cfg->limit_uid > 0 && uid_req_count > cfg->limit_uid) { ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r, "Rejected, too many connections for uid %u (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 && vhost_req_count > 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; } /* Parse the MaxConnPerIP directive */ static const char *limit_config_cmd(cmd_parms *parms, void *mconfig, const char *arg1, const char *arg2) { limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module); long int limit, limit_ka; limit = strtol(arg1, (char **)NULL, 10); if (limit < 0 || limit > INT_MAX) return "Integer overflow or invalid number"; if (arg2) { 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; } cfg->limit = limit; return NULL; } #if CREATE_OWN_SCOREBOARD /* Parse the IPLimit directive */ static const char *excl_limit_config_cmd(cmd_parms *parms, void *mconfig, const char *arg) { limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module); *(char **)apr_array_push(cfg->excl_limit) = apr_pstrdup(parms->pool, arg); return NULL; } #endif /* Parse the MaxConnPerVhost directive */ static const char *limit_vhost_config_cmd(cmd_parms *parms, void *mconfig, const char *arg1, const char *arg2) { limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module); long int limit, limit_ka; limit = strtol(arg1, (char **)NULL, 10); if (limit < 0 || limit > INT_MAX) return "Integer overflow or invalid number"; if (arg2) { 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; } cfg->limit_vhost = limit; return NULL; } #if CREATE_OWN_SCOREBOARD /* Parse the MaxConnPerUid directive */ static const char *limit_uid_config_cmd(cmd_parms *parms, void *mconfig, const char *arg1, const char *arg2) { limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module); long int limit, limit_ka; limit = strtol(arg1, (char **)NULL, 10); if (limit < 0 || limit > INT_MAX) return "Integer overflow or invalid number"; if (arg2) { 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; } cfg->limit_uid = limit; return NULL; } #endif /* Parse the MaxLA1 directive */ static const char *limit_la1_config_cmd(cmd_parms *parms, void *mconfig, const char *arg) { limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->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 *parms, void *mconfig, const char *arg) { limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->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 *parms, void *mconfig, const char *arg) { limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->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 *parms, void *mconfig, const char *arg) { const char *err = ap_check_cmd_context(parms, GLOBAL_ONLY); if (err != NULL) return err; if (parms->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."), #if CREATE_OWN_SCOREBOARD AP_INIT_ITERATE("IPLimit", excl_limit_config_cmd, NULL, RSRC_CONF, "restrict limit checking to these handlers/MIME types only."), #endif #if 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 }, }; #if CREATE_OWN_SCOREBOARD static apr_status_t limit_detach_shm(void *data) { shmdt(data); return APR_SUCCESS; } #endif static int limitipconn_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { void *data; const char *userdata_key = "limitipconn_init"; /* 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. */ apr_pool_userdata_get(&data, userdata_key, s->process->pool); if (!data) { apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool); return APR_SUCCESS; } ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit); ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit); #if CREATE_OWN_SCOREBOARD if ((limit_shmid = shmget(SHM_KEY, sizeof(limit_scoreboard) * 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); } ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_NOTICE, 0), s, "mod_limitipconn: initialized shared memory."); //if ((sb = (limit_scoreboard *) shmat(limit_shmid, NULL, 0)) == (limit_scoreboard *) -1) if ((sb = (void *)shmat(limit_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 *)sb, limit_detach_shm, apr_pool_cleanup_null); /* Mark the segment for deletion once all attachments are detached */ if (shmctl(limit_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 (sb == (void *) -1) exit(1); #endif ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_NOTICE, 0), s, MODULE_NAME " " MODULE_VERSION " started."); return APR_SUCCESS; } 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 */ };