| /* 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 |
| * |
| * 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. |
| */ |
| |
| /* Memory handler for a shared memory divided in slot. |
| * This one uses shared memory. |
| * |
| * Shared memory is cleaned-up for each restart, graceful or |
| * otherwise. |
| */ |
| |
| #include "ap_slotmem.h" |
| |
| #include "httpd.h" |
| #include "http_main.h" |
| #include "http_core.h" |
| |
| #define AP_SLOTMEM_IS_PREGRAB(t) (t->desc->type & AP_SLOTMEM_TYPE_PREGRAB) |
| #define AP_SLOTMEM_IS_PERSIST(t) (t->desc->type & AP_SLOTMEM_TYPE_PERSIST) |
| #define AP_SLOTMEM_IS_CLEARINUSE(t) (t->desc->type & AP_SLOTMEM_TYPE_CLEARINUSE) |
| |
| /* The description of the slots to reuse the slotmem */ |
| typedef struct { |
| apr_size_t size; /* size of each memory slot */ |
| unsigned int num; /* number of mem slots */ |
| ap_slotmem_type_t type; /* type-specific flags */ |
| } sharedslotdesc_t; |
| |
| #define AP_SLOTMEM_OFFSET (APR_ALIGN_DEFAULT(sizeof(sharedslotdesc_t))) |
| #define AP_UNSIGNEDINT_OFFSET (APR_ALIGN_DEFAULT(sizeof(unsigned int))) |
| |
| struct ap_slotmem_instance_t { |
| char *name; /* file based SHM path/name */ |
| char *pname; /* persisted file path/name */ |
| int fbased; /* filebased? */ |
| void *shm; /* ptr to memory segment (apr_shm_t *) */ |
| void *base; /* data set start */ |
| apr_pool_t *pool; /* per segment pool (generation cleared) */ |
| char *inuse; /* in-use flag table*/ |
| unsigned int *num_free; /* slot free count for this instance */ |
| void *persist; /* persist dataset start */ |
| const sharedslotdesc_t *desc; /* per slot desc */ |
| struct ap_slotmem_instance_t *next; /* location of next allocated segment */ |
| }; |
| |
| /* |
| * Layout for SHM and persited file : |
| * |
| * +-------------------------------------------------------------+~> |
| * | desc | num_free | base (slots) | inuse (array) | md5 | desc | compat.. |
| * +------+-----------------------------------------+------------+~> |
| * ^ ^ ^ \ / ^ : |
| * |______|_____________ SHM (mem->@) ______________| | _____|__/ |
| * | |/ | |
| * | ^ v | |
| * |_____________________ File (mem->persist + [meta]) __| |
| */ |
| |
| |
| /* global pool and list of slotmem we are handling */ |
| static struct ap_slotmem_instance_t *globallistmem = NULL, |
| **retained_globallistmem = NULL; |
| static apr_pool_t *gpool = NULL; |
| |
| #define DEFAULT_SLOTMEM_PREFIX "slotmem-shm-" |
| #define DEFAULT_SLOTMEM_SUFFIX ".shm" |
| #define DEFAULT_SLOTMEM_PERSIST_SUFFIX ".persist" |
| |
| /* |
| * Persist the slotmem in a file |
| * slotmem name and file name. |
| * none : no persistent data |
| * rel_name : $server_root/rel_name |
| * /abs_name : $abs_name |
| * |
| */ |
| static int slotmem_filenames(apr_pool_t *pool, |
| const char *slotname, |
| const char **filename, |
| const char **persistname) |
| { |
| const char *fname = NULL, *pname = NULL; |
| |
| if (slotname && *slotname && strcasecmp(slotname, "none") != 0) { |
| if (slotname[0] != '/') { |
| fname = apr_pstrcat(pool, DEFAULT_SLOTMEM_PREFIX, |
| slotname, DEFAULT_SLOTMEM_SUFFIX, |
| NULL); |
| fname = ap_runtime_dir_relative(pool, fname); |
| } |
| else { |
| /* Don't mangle the file name if given an absolute path, it's |
| * up to the caller to provide a unique name when necessary. |
| */ |
| fname = slotname; |
| } |
| |
| if (persistname) { |
| pname = apr_pstrcat(pool, fname, |
| DEFAULT_SLOTMEM_PERSIST_SUFFIX, |
| NULL); |
| } |
| } |
| |
| *filename = fname; |
| if (persistname) { |
| *persistname = pname; |
| } |
| return (fname != NULL); |
| } |
| |
| static void slotmem_clearinuse(ap_slotmem_instance_t *slot) |
| { |
| unsigned int i; |
| char *inuse; |
| |
| if (!slot) { |
| return; |
| } |
| |
| inuse = slot->inuse; |
| |
| for (i = 0; i < slot->desc->num; i++, inuse++) { |
| if (*inuse) { |
| *inuse = 0; |
| (*slot->num_free)++; |
| } |
| } |
| } |
| |
| static void store_slotmem(ap_slotmem_instance_t *slotmem) |
| { |
| apr_file_t *fp; |
| apr_status_t rv; |
| apr_size_t nbytes; |
| unsigned char digest[APR_MD5_DIGESTSIZE]; |
| const char *storename = slotmem->pname; |
| |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02334) |
| "storing %s", storename); |
| |
| if (storename) { |
| rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE, |
| APR_OS_DEFAULT, slotmem->pool); |
| if (APR_STATUS_IS_EEXIST(rv)) { |
| apr_file_remove(storename, slotmem->pool); |
| rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE, |
| APR_OS_DEFAULT, slotmem->pool); |
| } |
| if (rv != APR_SUCCESS) { |
| return; |
| } |
| if (AP_SLOTMEM_IS_CLEARINUSE(slotmem)) { |
| slotmem_clearinuse(slotmem); |
| } |
| nbytes = (slotmem->desc->size * slotmem->desc->num) + |
| (slotmem->desc->num * sizeof(char)) + AP_UNSIGNEDINT_OFFSET; |
| apr_md5(digest, slotmem->persist, nbytes); |
| rv = apr_file_write_full(fp, slotmem->persist, nbytes, NULL); |
| if (rv == APR_SUCCESS) { |
| rv = apr_file_write_full(fp, digest, APR_MD5_DIGESTSIZE, NULL); |
| } |
| if (rv == APR_SUCCESS) { |
| rv = apr_file_write_full(fp, slotmem->desc, AP_SLOTMEM_OFFSET, |
| NULL); |
| } |
| apr_file_close(fp); |
| if (rv != APR_SUCCESS) { |
| apr_file_remove(storename, slotmem->pool); |
| } |
| } |
| } |
| |
| static apr_status_t restore_slotmem(sharedslotdesc_t *desc, |
| const char *storename, apr_size_t size, |
| apr_pool_t *pool) |
| { |
| apr_file_t *fp; |
| apr_status_t rv = APR_ENOTIMPL; |
| void *ptr = (char *)desc + AP_SLOTMEM_OFFSET; |
| apr_size_t dsize = size - AP_SLOTMEM_OFFSET; |
| apr_size_t nbytes = dsize; |
| unsigned char digest[APR_MD5_DIGESTSIZE]; |
| unsigned char digest2[APR_MD5_DIGESTSIZE]; |
| char desc_buf[AP_SLOTMEM_OFFSET]; |
| |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02335) |
| "restoring %s", storename); |
| |
| if (storename) { |
| rv = apr_file_open(&fp, storename, APR_READ | APR_WRITE, APR_OS_DEFAULT, |
| pool); |
| if (rv == APR_SUCCESS) { |
| rv = apr_file_read(fp, ptr, &nbytes); |
| if ((rv == APR_SUCCESS || rv == APR_EOF) && nbytes == dsize) { |
| rv = APR_SUCCESS; /* for successful return @ EOF */ |
| /* |
| * if at EOF, don't bother checking md5 |
| * - backwards compatibility |
| * */ |
| if (apr_file_eof(fp) != APR_EOF) { |
| apr_size_t ds = APR_MD5_DIGESTSIZE; |
| rv = apr_file_read(fp, digest, &ds); |
| if ((rv == APR_SUCCESS || rv == APR_EOF) |
| && ds == APR_MD5_DIGESTSIZE) { |
| apr_md5(digest2, ptr, nbytes); |
| if (memcmp(digest, digest2, APR_MD5_DIGESTSIZE)) { |
| rv = APR_EMISMATCH; |
| } |
| /* |
| * if at EOF, don't bother checking desc |
| * - backwards compatibility |
| * */ |
| else if (apr_file_eof(fp) != APR_EOF) { |
| nbytes = sizeof(desc_buf); |
| rv = apr_file_read(fp, desc_buf, &nbytes); |
| if ((rv == APR_SUCCESS || rv == APR_EOF) |
| && nbytes == sizeof(desc_buf)) { |
| if (memcmp(desc, desc_buf, nbytes)) { |
| rv = APR_EMISMATCH; |
| } |
| else { |
| rv = APR_SUCCESS; |
| } |
| } |
| else if (rv == APR_SUCCESS || rv == APR_EOF) { |
| rv = APR_INCOMPLETE; |
| } |
| } |
| else { |
| rv = APR_EOF; |
| } |
| } |
| else if (rv == APR_SUCCESS || rv == APR_EOF) { |
| rv = APR_INCOMPLETE; |
| } |
| } |
| else { |
| rv = APR_EOF; |
| } |
| if (rv == APR_EMISMATCH) { |
| ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(02551) |
| "persisted slotmem md5/desc mismatch"); |
| } |
| else if (rv == APR_EOF) { |
| ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(02552) |
| "persisted slotmem at EOF... bypassing md5/desc match check " |
| "(old persist file?)"); |
| rv = APR_SUCCESS; |
| } |
| } |
| else if (rv == APR_SUCCESS || rv == APR_EOF) { |
| rv = APR_INCOMPLETE; |
| } |
| if (rv == APR_INCOMPLETE) { |
| ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(02553) |
| "persisted slotmem read had unexpected size"); |
| } |
| apr_file_close(fp); |
| } |
| } |
| return rv; |
| } |
| |
| static apr_status_t cleanup_slotmem(void *is_startup) |
| { |
| int is_exiting = (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_EXITING); |
| ap_slotmem_instance_t *mem; |
| |
| /* When in startup/pre-config's cleanup, the retained data and global pool |
| * are not used yet, but the SHMs contents were untouched hence they don't |
| * need to be persisted, simply unlink them. |
| * Otherwise when restarting or stopping we want to flush persisted data, |
| * and in the stopping/exiting case we also want to unlink the SHMs. |
| */ |
| for (mem = globallistmem; mem; mem = mem->next) { |
| int unlink; |
| if (is_startup) { |
| unlink = mem->fbased; |
| } |
| else { |
| if (AP_SLOTMEM_IS_PERSIST(mem)) { |
| store_slotmem(mem); |
| } |
| unlink = is_exiting; |
| } |
| if (unlink) { |
| /* Some systems may require the descriptor to be closed before |
| * unlink, thus call destroy() first. |
| */ |
| apr_shm_destroy(mem->shm); |
| apr_shm_remove(mem->name, mem->pool); |
| } |
| } |
| |
| if (is_exiting) { |
| *retained_globallistmem = NULL; |
| } |
| else if (!is_startup) { |
| *retained_globallistmem = globallistmem; |
| } |
| globallistmem = NULL; |
| |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t slotmem_doall(ap_slotmem_instance_t *mem, |
| ap_slotmem_callback_fn_t *func, |
| void *data, apr_pool_t *pool) |
| { |
| unsigned int i; |
| char *ptr; |
| char *inuse; |
| apr_status_t retval = APR_SUCCESS; |
| |
| if (!mem) { |
| return APR_ENOSHMAVAIL; |
| } |
| |
| ptr = (char *)mem->base; |
| inuse = mem->inuse; |
| for (i = 0; i < mem->desc->num; i++, inuse++) { |
| if (!AP_SLOTMEM_IS_PREGRAB(mem) || *inuse) { |
| retval = func((void *) ptr, data, pool); |
| if (retval != APR_SUCCESS) |
| break; |
| } |
| ptr += mem->desc->size; |
| } |
| return retval; |
| } |
| |
| static int check_slotmem(ap_slotmem_instance_t *mem, apr_size_t size, |
| apr_size_t item_size, unsigned int item_num) |
| { |
| sharedslotdesc_t *desc; |
| |
| /* check size */ |
| if (apr_shm_size_get(mem->shm) != size) { |
| ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(02599) |
| "existing shared memory for %s could not be used " |
| "(failed size check)", |
| mem->name); |
| return 0; |
| } |
| |
| desc = apr_shm_baseaddr_get(mem->shm); |
| if (desc->size != item_size || desc->num != item_num) { |
| ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(02600) |
| "existing shared memory for %s could not be used " |
| "(failed contents check)", |
| mem->name); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static apr_status_t slotmem_create(ap_slotmem_instance_t **new, |
| const char *name, apr_size_t item_size, |
| unsigned int item_num, |
| ap_slotmem_type_t type, apr_pool_t *pool) |
| { |
| int fbased = 1; |
| int restored = 0; |
| char *ptr; |
| sharedslotdesc_t *desc; |
| ap_slotmem_instance_t *res; |
| ap_slotmem_instance_t *next = globallistmem; |
| const char *fname, *pname = NULL; |
| apr_shm_t *shm; |
| apr_size_t basesize = (item_size * item_num); |
| apr_size_t size = AP_SLOTMEM_OFFSET + AP_UNSIGNEDINT_OFFSET + |
| (item_num * sizeof(char)) + basesize; |
| int persist = (type & AP_SLOTMEM_TYPE_PERSIST) != 0; |
| apr_status_t rv; |
| apr_pool_t *p; |
| |
| *new = NULL; |
| |
| if (slotmem_filenames(pool, name, &fname, persist ? &pname : NULL)) { |
| /* first try to attach to existing slotmem */ |
| if (next) { |
| ap_slotmem_instance_t *prev = NULL; |
| for (;;) { |
| if (strcmp(next->name, fname) == 0) { |
| *new = next; /* either returned here or reused finally */ |
| if (!check_slotmem(next, size, item_size, item_num)) { |
| apr_shm_destroy(next->shm); |
| next = next->next; |
| if (prev) { |
| prev->next = next; |
| } |
| else { |
| globallistmem = next; |
| } |
| if (next) { |
| continue; |
| } |
| next = prev; |
| break; |
| } |
| /* we already have it */ |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02603) |
| "create found %s in global list", fname); |
| return APR_SUCCESS; |
| } |
| if (!next->next) { |
| break; |
| } |
| prev = next; |
| next = next->next; |
| } |
| } |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02602) |
| "create didn't find %s in global list", fname); |
| } |
| else { |
| fbased = 0; |
| fname = "none"; |
| } |
| |
| /* first try to attach to existing shared memory */ |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02300) |
| "create %s: %"APR_SIZE_T_FMT"/%u", fname, item_size, |
| item_num); |
| |
| { |
| if (fbased) { |
| apr_shm_remove(fname, pool); |
| rv = apr_shm_create(&shm, size, fname, gpool); |
| } |
| else { |
| rv = apr_shm_create(&shm, size, NULL, pool); |
| } |
| ap_log_error(APLOG_MARK, rv == APR_SUCCESS ? APLOG_DEBUG : APLOG_ERR, |
| rv, ap_server_conf, APLOGNO(02611) |
| "create: apr_shm_create(%s) %s", |
| fname ? fname : "", |
| rv == APR_SUCCESS ? "succeeded" : "failed"); |
| if (rv != APR_SUCCESS) { |
| return rv; |
| } |
| |
| desc = (sharedslotdesc_t *)apr_shm_baseaddr_get(shm); |
| memset(desc, 0, size); |
| desc->size = item_size; |
| desc->num = item_num; |
| desc->type = type; |
| |
| /* |
| * TODO: Error check the below... What error makes |
| * sense if the restore fails? Any? |
| * For now, we continue with a fresh new slotmem, |
| * but NOTICE in the log. |
| */ |
| if (persist) { |
| rv = restore_slotmem(desc, pname, size, pool); |
| if (rv == APR_SUCCESS) { |
| restored = 1; |
| } |
| else { |
| /* just in case, re-zero */ |
| ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, |
| APLOGNO(02554) "could not restore %s", fname); |
| memset((char *)desc + AP_SLOTMEM_OFFSET, 0, |
| size - AP_SLOTMEM_OFFSET); |
| } |
| } |
| } |
| |
| p = fbased ? gpool : pool; |
| ptr = (char *)desc + AP_SLOTMEM_OFFSET; |
| |
| /* For the chained slotmem stuff (*new may be reused from above) */ |
| res = *new; |
| if (res == NULL) { |
| res = apr_pcalloc(p, sizeof(ap_slotmem_instance_t)); |
| res->name = apr_pstrdup(p, fname); |
| res->pname = apr_pstrdup(p, pname); |
| *new = res; |
| } |
| res->fbased = fbased; |
| res->shm = shm; |
| res->persist = (void *)ptr; |
| res->num_free = (unsigned int *)ptr; |
| ptr += AP_UNSIGNEDINT_OFFSET; |
| if (!restored) { |
| *res->num_free = item_num; |
| } |
| res->base = (void *)ptr; |
| res->desc = desc; |
| res->pool = pool; |
| res->next = NULL; |
| res->inuse = ptr + basesize; |
| if (fbased) { |
| if (globallistmem == NULL) { |
| globallistmem = res; |
| } |
| else { |
| next->next = res; |
| } |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t slotmem_attach(ap_slotmem_instance_t **new, |
| const char *name, apr_size_t *item_size, |
| unsigned int *item_num, apr_pool_t *pool) |
| { |
| /* void *slotmem = NULL; */ |
| char *ptr; |
| ap_slotmem_instance_t *res; |
| ap_slotmem_instance_t *next = globallistmem; |
| sharedslotdesc_t *desc; |
| const char *fname; |
| apr_shm_t *shm; |
| apr_status_t rv; |
| |
| if (!slotmem_filenames(pool, name, &fname, NULL)) { |
| return APR_ENOSHMAVAIL; |
| } |
| |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02301) |
| "attach looking for %s", fname); |
| |
| /* first try to attach to existing slotmem */ |
| if (next) { |
| for (;;) { |
| if (strcmp(next->name, fname) == 0) { |
| /* we already have it */ |
| *new = next; |
| *item_size = next->desc->size; |
| *item_num = next->desc->num; |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, |
| APLOGNO(02302) |
| "attach found %s: %"APR_SIZE_T_FMT"/%u", fname, |
| *item_size, *item_num); |
| return APR_SUCCESS; |
| } |
| if (!next->next) { |
| break; |
| } |
| next = next->next; |
| } |
| } |
| |
| /* next try to attach to existing shared memory */ |
| rv = apr_shm_attach(&shm, fname, pool); |
| if (rv != APR_SUCCESS) { |
| return rv; |
| } |
| |
| /* Read the description of the slotmem */ |
| desc = (sharedslotdesc_t *)apr_shm_baseaddr_get(shm); |
| ptr = (char *)desc + AP_SLOTMEM_OFFSET; |
| |
| /* For the chained slotmem stuff */ |
| res = apr_pcalloc(pool, sizeof(ap_slotmem_instance_t)); |
| res->name = apr_pstrdup(pool, fname); |
| res->fbased = 1; |
| res->shm = shm; |
| res->persist = (void *)ptr; |
| res->num_free = (unsigned int *)ptr; |
| ptr += AP_UNSIGNEDINT_OFFSET; |
| res->base = (void *)ptr; |
| res->desc = desc; |
| res->pool = pool; |
| res->inuse = ptr + (desc->size * desc->num); |
| res->next = NULL; |
| |
| *new = res; |
| *item_size = desc->size; |
| *item_num = desc->num; |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, |
| APLOGNO(02303) |
| "attach found %s: %"APR_SIZE_T_FMT"/%u", fname, |
| *item_size, *item_num); |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t slotmem_dptr(ap_slotmem_instance_t *slot, |
| unsigned int id, void **mem) |
| { |
| char *ptr; |
| |
| if (!slot) { |
| return APR_ENOSHMAVAIL; |
| } |
| if (id >= slot->desc->num) { |
| return APR_EINVAL; |
| } |
| |
| ptr = (char *)slot->base + slot->desc->size * id; |
| if (!ptr) { |
| return APR_ENOSHMAVAIL; |
| } |
| *mem = (void *)ptr; |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t slotmem_get(ap_slotmem_instance_t *slot, unsigned int id, |
| unsigned char *dest, apr_size_t dest_len) |
| { |
| void *ptr; |
| char *inuse; |
| apr_status_t ret; |
| |
| if (!slot) { |
| return APR_ENOSHMAVAIL; |
| } |
| |
| inuse = slot->inuse + id; |
| if (id >= slot->desc->num) { |
| return APR_EINVAL; |
| } |
| if (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse) { |
| return APR_NOTFOUND; |
| } |
| ret = slotmem_dptr(slot, id, &ptr); |
| if (ret != APR_SUCCESS) { |
| return ret; |
| } |
| *inuse = 1; |
| memcpy(dest, ptr, dest_len); /* bounds check? */ |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t slotmem_put(ap_slotmem_instance_t *slot, unsigned int id, |
| unsigned char *src, apr_size_t src_len) |
| { |
| void *ptr; |
| char *inuse; |
| apr_status_t ret; |
| |
| if (!slot) { |
| return APR_ENOSHMAVAIL; |
| } |
| |
| inuse = slot->inuse + id; |
| if (id >= slot->desc->num) { |
| return APR_EINVAL; |
| } |
| if (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse) { |
| return APR_NOTFOUND; |
| } |
| ret = slotmem_dptr(slot, id, &ptr); |
| if (ret != APR_SUCCESS) { |
| return ret; |
| } |
| *inuse=1; |
| memcpy(ptr, src, src_len); /* bounds check? */ |
| return APR_SUCCESS; |
| } |
| |
| static unsigned int slotmem_num_slots(ap_slotmem_instance_t *slot) |
| { |
| return slot->desc->num; |
| } |
| |
| static unsigned int slotmem_num_free_slots(ap_slotmem_instance_t *slot) |
| { |
| if (AP_SLOTMEM_IS_PREGRAB(slot)) |
| return *slot->num_free; |
| else { |
| unsigned int i, counter=0; |
| char *inuse = slot->inuse; |
| for (i=0; i<slot->desc->num; i++, inuse++) { |
| if (!*inuse) |
| counter++; |
| } |
| return counter; |
| } |
| } |
| |
| static apr_size_t slotmem_slot_size(ap_slotmem_instance_t *slot) |
| { |
| return slot->desc->size; |
| } |
| |
| static apr_status_t slotmem_grab(ap_slotmem_instance_t *slot, unsigned int *id) |
| { |
| unsigned int i; |
| char *inuse; |
| |
| if (!slot) { |
| return APR_ENOSHMAVAIL; |
| } |
| |
| inuse = slot->inuse; |
| |
| for (i = 0; i < slot->desc->num; i++, inuse++) { |
| if (!*inuse) { |
| break; |
| } |
| } |
| if (i >= slot->desc->num) { |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02293) |
| "slotmem(%s) grab failed. Num %u/num_free %u", |
| slot->name, slotmem_num_slots(slot), |
| slotmem_num_free_slots(slot)); |
| return APR_EINVAL; |
| } |
| *inuse = 1; |
| *id = i; |
| (*slot->num_free)--; |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t slotmem_fgrab(ap_slotmem_instance_t *slot, unsigned int id) |
| { |
| char *inuse; |
| |
| if (!slot) { |
| return APR_ENOSHMAVAIL; |
| } |
| |
| if (id >= slot->desc->num) { |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02397) |
| "slotmem(%s) fgrab failed. Num %u/num_free %u", |
| slot->name, slotmem_num_slots(slot), |
| slotmem_num_free_slots(slot)); |
| return APR_EINVAL; |
| } |
| inuse = slot->inuse + id; |
| |
| if (!*inuse) { |
| *inuse = 1; |
| (*slot->num_free)--; |
| } |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t slotmem_release(ap_slotmem_instance_t *slot, |
| unsigned int id) |
| { |
| char *inuse; |
| |
| if (!slot) { |
| return APR_ENOSHMAVAIL; |
| } |
| |
| inuse = slot->inuse; |
| |
| if (id >= slot->desc->num || !inuse[id] ) { |
| ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02294) |
| "slotmem(%s) release failed. Num %u/inuse[%u] %d", |
| slot->name, slotmem_num_slots(slot), |
| id, (int)inuse[id]); |
| if (id >= slot->desc->num) { |
| return APR_EINVAL; |
| } else { |
| return APR_NOTFOUND; |
| } |
| } |
| inuse[id] = 0; |
| (*slot->num_free)++; |
| return APR_SUCCESS; |
| } |
| |
| static const ap_slotmem_provider_t storage = { |
| "sharedmem", |
| &slotmem_doall, |
| &slotmem_create, |
| &slotmem_attach, |
| &slotmem_dptr, |
| &slotmem_get, |
| &slotmem_put, |
| &slotmem_num_slots, |
| &slotmem_num_free_slots, |
| &slotmem_slot_size, |
| &slotmem_grab, |
| &slotmem_release, |
| &slotmem_fgrab |
| }; |
| |
| /* make the storage usable from outside */ |
| static const ap_slotmem_provider_t *slotmem_shm_getstorage(void) |
| { |
| return (&storage); |
| } |
| |
| /* Initialize or reuse the retained slotmems list, and register the |
| * cleanup to make sure the persisted SHMs are stored and the retained |
| * data are up to date on next restart/stop. |
| */ |
| static int pre_config(apr_pool_t *pconf, apr_pool_t *plog, |
| apr_pool_t *ptemp) |
| { |
| void *is_startup = NULL; |
| const char *retained_key = "mod_slotmem_shm"; |
| |
| retained_globallistmem = ap_retained_data_get(retained_key); |
| if (!retained_globallistmem) { |
| retained_globallistmem = |
| ap_retained_data_create(retained_key, |
| sizeof *retained_globallistmem); |
| } |
| globallistmem = *retained_globallistmem; |
| |
| if (ap_state_query(AP_SQ_MAIN_STATE) != AP_SQ_MS_CREATE_PRE_CONFIG) { |
| gpool = ap_pglobal; |
| } |
| else { |
| is_startup = (void *)1; |
| gpool = pconf; |
| } |
| |
| apr_pool_cleanup_register(pconf, is_startup, cleanup_slotmem, |
| apr_pool_cleanup_null); |
| |
| return OK; |
| } |
| |
| static void ap_slotmem_shm_register_hook(apr_pool_t *p) |
| { |
| const ap_slotmem_provider_t *storage = slotmem_shm_getstorage(); |
| ap_register_provider(p, AP_SLOTMEM_PROVIDER_GROUP, "shm", |
| AP_SLOTMEM_PROVIDER_VERSION, storage); |
| ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE); |
| } |
| |
| AP_DECLARE_MODULE(slotmem_shm) = { |
| STANDARD20_MODULE_STUFF, |
| NULL, /* create per-directory config structure */ |
| NULL, /* merge per-directory config structures */ |
| NULL, /* create per-server config structure */ |
| NULL, /* merge per-server config structures */ |
| NULL, /* command apr_table_t */ |
| ap_slotmem_shm_register_hook /* register hooks */ |
| }; |