1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* Memory handler for a shared memory divided in slot.
18 * This one uses shared memory.
20 * Shared memory is cleaned-up for each restart, graceful or
24 #include "ap_slotmem.h"
27 #include "http_main.h"
28 #ifdef AP_NEED_SET_MUTEX_PERMS
33 #include <unistd.h> /* for getpid() */
46 #define AP_SLOTMEM_IS_PREGRAB(t) (t->desc.type & AP_SLOTMEM_TYPE_PREGRAB)
47 #define AP_SLOTMEM_IS_PERSIST(t) (t->desc.type & AP_SLOTMEM_TYPE_PERSIST)
48 #define AP_SLOTMEM_IS_CLEARINUSE(t) (t->desc.type & AP_SLOTMEM_TYPE_CLEARINUSE)
50 /* The description of the slots to reuse the slotmem */
52 apr_size_t size; /* size of each memory slot */
53 unsigned int num; /* number of mem slots */
54 ap_slotmem_type_t type; /* type-specific flags */
57 #define AP_SLOTMEM_OFFSET (APR_ALIGN_DEFAULT(sizeof(sharedslotdesc_t)))
58 #define AP_UNSIGNEDINT_OFFSET (APR_ALIGN_DEFAULT(sizeof(unsigned int)))
60 struct ap_slotmem_instance_t {
61 char *name; /* per segment name */
62 int fbased; /* filebased? */
63 void *shm; /* ptr to memory segment (apr_shm_t *) */
64 void *base; /* data set start */
65 apr_pool_t *gpool; /* per segment global pool */
66 char *inuse; /* in-use flag table*/
67 unsigned int *num_free; /* slot free count for this instance */
68 void *persist; /* persist dataset start */
69 sharedslotdesc_t desc; /* per slot desc */
70 struct ap_slotmem_instance_t *next; /* location of next allocated segment */
75 * sharedslotdesc_t | num_free | slots | isuse array |
78 * . persist (also num_free)
81 /* global pool and list of slotmem we are handling */
82 static struct ap_slotmem_instance_t *globallistmem = NULL;
83 static apr_pool_t *gpool = NULL;
85 #define DEFAULT_SLOTMEM_PREFIX "slotmem-shm-"
86 #define DEFAULT_SLOTMEM_SUFFIX ".shm"
87 #define DEFAULT_SLOTMEM_PERSIST_SUFFIX ".persist"
89 /* apr:shmem/unix/shm.c */
90 static apr_status_t unixd_set_shm_perms(const char *fname)
92 #ifdef AP_NEED_SET_MUTEX_PERMS
93 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
94 struct shmid_ds shmbuf;
98 shmkey = ftok(fname, 1);
99 if (shmkey == (key_t)-1) {
102 if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
105 #if MODULE_MAGIC_NUMBER_MAJOR <= 20081212
106 #define ap_unixd_config unixd_config
108 shmbuf.shm_perm.uid = ap_unixd_config.user_id;
109 shmbuf.shm_perm.gid = ap_unixd_config.group_id;
110 shmbuf.shm_perm.mode = 0600;
111 if (shmctl(shmid, IPC_SET, &shmbuf) == -1) {
124 * Persist the slotmem in a file
125 * slotmem name and file name.
126 * none : no persistent data
127 * rel_name : $server_root/rel_name
128 * /abs_name : $abs_name
132 static const char *slotmem_filename(apr_pool_t *pool, const char *slotmemname,
136 if (!slotmemname || strcasecmp(slotmemname, "none") == 0) {
139 else if (slotmemname[0] != '/') {
140 const char *filenm = apr_pstrcat(pool, DEFAULT_SLOTMEM_PREFIX,
141 slotmemname, DEFAULT_SLOTMEM_SUFFIX,
143 fname = ap_runtime_dir_relative(pool, filenm);
150 return apr_pstrcat(pool, fname, DEFAULT_SLOTMEM_PERSIST_SUFFIX,
156 static void slotmem_clearinuse(ap_slotmem_instance_t *slot)
167 for (i = 0; i < slot->desc.num; i++, inuse++) {
175 static void store_slotmem(ap_slotmem_instance_t *slotmem)
180 const char *storename;
182 storename = slotmem_filename(slotmem->gpool, slotmem->name, 1);
184 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02334)
185 "storing %s", storename);
188 rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE,
189 APR_OS_DEFAULT, slotmem->gpool);
190 if (APR_STATUS_IS_EEXIST(rv)) {
191 apr_file_remove(storename, slotmem->gpool);
192 rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE,
193 APR_OS_DEFAULT, slotmem->gpool);
195 if (rv != APR_SUCCESS) {
198 if (AP_SLOTMEM_IS_CLEARINUSE(slotmem)) {
199 slotmem_clearinuse(slotmem);
201 nbytes = (slotmem->desc.size * slotmem->desc.num) +
202 (slotmem->desc.num * sizeof(char)) + AP_UNSIGNEDINT_OFFSET;
203 /* XXX: Error handling */
204 apr_file_write_full(fp, slotmem->persist, nbytes, NULL);
209 /* should be apr_status_t really */
210 static void restore_slotmem(void *ptr, const char *name, apr_size_t size,
213 const char *storename;
215 apr_size_t nbytes = size;
218 storename = slotmem_filename(pool, name, 1);
220 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02335)
221 "restoring %s", storename);
224 rv = apr_file_open(&fp, storename, APR_READ | APR_WRITE, APR_OS_DEFAULT,
226 if (rv == APR_SUCCESS) {
228 if (apr_file_info_get(&fi, APR_FINFO_SIZE, fp) == APR_SUCCESS) {
229 if (fi.size == nbytes) {
230 apr_file_read(fp, ptr, &nbytes);
234 apr_file_remove(storename, pool);
243 static apr_status_t cleanup_slotmem(void *param)
245 ap_slotmem_instance_t **mem = param;
248 ap_slotmem_instance_t *next = *mem;
250 if (AP_SLOTMEM_IS_PERSIST(next)) {
254 apr_shm_remove(next->name, next->gpool);
256 apr_shm_destroy((apr_shm_t *)next->shm);
260 /* apr_pool_destroy(gpool); */
261 globallistmem = NULL;
265 static apr_status_t slotmem_doall(ap_slotmem_instance_t *mem,
266 ap_slotmem_callback_fn_t *func,
267 void *data, apr_pool_t *pool)
272 apr_status_t retval = APR_SUCCESS;
275 return APR_ENOSHMAVAIL;
278 ptr = (char *)mem->base;
280 for (i = 0; i < mem->desc.num; i++, inuse++) {
281 if (!AP_SLOTMEM_IS_PREGRAB(mem) ||
282 (AP_SLOTMEM_IS_PREGRAB(mem) && *inuse)) {
283 retval = func((void *) ptr, data, pool);
284 if (retval != APR_SUCCESS)
287 ptr += mem->desc.size;
292 static apr_status_t slotmem_create(ap_slotmem_instance_t **new,
293 const char *name, apr_size_t item_size,
294 unsigned int item_num,
295 ap_slotmem_type_t type, apr_pool_t *pool)
297 /* void *slotmem = NULL; */
300 sharedslotdesc_t desc;
301 ap_slotmem_instance_t *res;
302 ap_slotmem_instance_t *next = globallistmem;
305 apr_size_t basesize = (item_size * item_num);
306 apr_size_t size = AP_SLOTMEM_OFFSET + AP_UNSIGNEDINT_OFFSET +
307 (item_num * sizeof(char)) + basesize;
311 return APR_ENOSHMAVAIL;
313 fname = slotmem_filename(pool, name, 0);
315 /* first try to attach to existing slotmem */
318 if (strcmp(next->name, fname) == 0) {
319 /* we already have it */
335 /* first try to attach to existing shared memory */
336 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02300)
337 "create %s: %"APR_SIZE_T_FMT"/%u", fname, item_size,
340 rv = apr_shm_attach(&shm, fname, gpool);
345 if (rv == APR_SUCCESS) {
347 if (apr_shm_size_get(shm) != size) {
351 ptr = (char *)apr_shm_baseaddr_get(shm);
352 memcpy(&desc, ptr, sizeof(desc));
353 if (desc.size != item_size || desc.num != item_num) {
357 ptr += AP_SLOTMEM_OFFSET;
360 apr_size_t dsize = size - AP_SLOTMEM_OFFSET;
362 apr_shm_remove(fname, gpool);
363 rv = apr_shm_create(&shm, size, fname, gpool);
366 rv = apr_shm_create(&shm, size, NULL, gpool);
368 if (rv != APR_SUCCESS) {
372 /* Set permissions to shared memory
373 * so it can be attached by child process
374 * having different user credentials
376 * See apr:shmem/unix/shm.c
378 unixd_set_shm_perms(fname);
380 ptr = (char *)apr_shm_baseaddr_get(shm);
381 desc.size = item_size;
384 memcpy(ptr, &desc, sizeof(desc));
385 ptr += AP_SLOTMEM_OFFSET;
386 memset(ptr, 0, dsize);
388 * TODO: Error check the below... What error makes
389 * sense if the restore fails? Any?
391 if (type & AP_SLOTMEM_TYPE_PERSIST) {
392 restore_slotmem(ptr, fname, dsize, pool);
396 /* For the chained slotmem stuff */
397 res = (ap_slotmem_instance_t *) apr_pcalloc(gpool,
398 sizeof(ap_slotmem_instance_t));
399 res->name = apr_pstrdup(gpool, fname);
400 res->fbased = fbased;
402 res->num_free = (unsigned int *)ptr;
403 *res->num_free = item_num;
404 res->persist = (void *)ptr;
405 ptr += AP_UNSIGNEDINT_OFFSET;
406 res->base = (void *)ptr;
410 res->inuse = ptr + basesize;
411 if (globallistmem == NULL) {
422 static apr_status_t slotmem_attach(ap_slotmem_instance_t **new,
423 const char *name, apr_size_t *item_size,
424 unsigned int *item_num, apr_pool_t *pool)
426 /* void *slotmem = NULL; */
428 ap_slotmem_instance_t *res;
429 ap_slotmem_instance_t *next = globallistmem;
430 sharedslotdesc_t desc;
436 return APR_ENOSHMAVAIL;
438 fname = slotmem_filename(pool, name, 0);
440 return APR_ENOSHMAVAIL;
443 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02301)
444 "attach looking for %s", fname);
446 /* first try to attach to existing slotmem */
449 if (strcmp(next->name, fname) == 0) {
450 /* we already have it */
452 *item_size = next->desc.size;
453 *item_num = next->desc.num;
454 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
456 "attach found %s: %"APR_SIZE_T_FMT"/%u", fname,
457 *item_size, *item_num);
467 /* next try to attach to existing shared memory */
468 rv = apr_shm_attach(&shm, fname, gpool);
469 if (rv != APR_SUCCESS) {
473 /* Read the description of the slotmem */
474 ptr = (char *)apr_shm_baseaddr_get(shm);
475 memcpy(&desc, ptr, sizeof(desc));
476 ptr += AP_SLOTMEM_OFFSET;
478 /* For the chained slotmem stuff */
479 res = (ap_slotmem_instance_t *) apr_pcalloc(gpool,
480 sizeof(ap_slotmem_instance_t));
481 res->name = apr_pstrdup(gpool, fname);
484 res->num_free = (unsigned int *)ptr;
485 res->persist = (void *)ptr;
486 ptr += AP_UNSIGNEDINT_OFFSET;
487 res->base = (void *)ptr;
490 res->inuse = ptr + (desc.size * desc.num);
492 if (globallistmem == NULL) {
500 *item_size = desc.size;
501 *item_num = desc.num;
502 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
504 "attach found %s: %"APR_SIZE_T_FMT"/%u", fname,
505 *item_size, *item_num);
509 static apr_status_t slotmem_dptr(ap_slotmem_instance_t *slot,
510 unsigned int id, void **mem)
515 return APR_ENOSHMAVAIL;
517 if (id >= slot->desc.num) {
521 ptr = (char *)slot->base + slot->desc.size * id;
523 return APR_ENOSHMAVAIL;
529 static apr_status_t slotmem_get(ap_slotmem_instance_t *slot, unsigned int id,
530 unsigned char *dest, apr_size_t dest_len)
537 return APR_ENOSHMAVAIL;
540 inuse = slot->inuse + id;
541 if (id >= slot->desc.num) {
544 if (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse) {
547 ret = slotmem_dptr(slot, id, &ptr);
548 if (ret != APR_SUCCESS) {
552 memcpy(dest, ptr, dest_len); /* bounds check? */
556 static apr_status_t slotmem_put(ap_slotmem_instance_t *slot, unsigned int id,
557 unsigned char *src, apr_size_t src_len)
564 return APR_ENOSHMAVAIL;
567 inuse = slot->inuse + id;
568 if (id >= slot->desc.num) {
571 if (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse) {
574 ret = slotmem_dptr(slot, id, &ptr);
575 if (ret != APR_SUCCESS) {
579 memcpy(ptr, src, src_len); /* bounds check? */
583 static unsigned int slotmem_num_slots(ap_slotmem_instance_t *slot)
585 return slot->desc.num;
588 static unsigned int slotmem_num_free_slots(ap_slotmem_instance_t *slot)
590 if (AP_SLOTMEM_IS_PREGRAB(slot))
591 return *slot->num_free;
593 unsigned int i, counter=0;
594 char *inuse = slot->inuse;
595 for (i=0; i<slot->desc.num; i++, inuse++) {
603 static apr_size_t slotmem_slot_size(ap_slotmem_instance_t *slot)
605 return slot->desc.size;
608 static apr_status_t slotmem_grab(ap_slotmem_instance_t *slot, unsigned int *id)
614 return APR_ENOSHMAVAIL;
619 for (i = 0; i < slot->desc.num; i++, inuse++) {
624 if (i >= slot->desc.num) {
625 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02293)
626 "slotmem(%s) grab failed. Num %u/num_free %u",
627 slot->name, slotmem_num_slots(slot),
628 slotmem_num_free_slots(slot));
637 static apr_status_t slotmem_fgrab(ap_slotmem_instance_t *slot, unsigned int id)
642 return APR_ENOSHMAVAIL;
645 if (id >= slot->desc.num) {
646 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02397)
647 "slotmem(%s) fgrab failed. Num %u/num_free %u",
648 slot->name, slotmem_num_slots(slot),
649 slotmem_num_free_slots(slot));
652 inuse = slot->inuse + id;
661 static apr_status_t slotmem_release(ap_slotmem_instance_t *slot,
667 return APR_ENOSHMAVAIL;
672 if (id >= slot->desc.num || !inuse[id] ) {
673 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02294)
674 "slotmem(%s) release failed. Num %u/inuse[%u] %d",
675 slot->name, slotmem_num_slots(slot),
677 if (id >= slot->desc.num) {
688 static const ap_slotmem_provider_t storage = {
697 &slotmem_num_free_slots,
704 /* make the storage usuable from outside */
705 static const ap_slotmem_provider_t *slotmem_shm_getstorage(void)
710 /* initialise the global pool */
711 static void slotmem_shm_initgpool(apr_pool_t *p)
716 /* Add the pool_clean routine */
717 static void slotmem_shm_initialize_cleanup(apr_pool_t *p)
719 apr_pool_cleanup_register(p, &globallistmem, cleanup_slotmem,
720 apr_pool_cleanup_null);
724 * Make sure the shared memory is cleaned
726 static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
729 slotmem_shm_initialize_cleanup(p);
733 static int pre_config(apr_pool_t *p, apr_pool_t *plog,
736 slotmem_shm_initgpool(p);
740 static void ap_slotmem_shm_register_hook(apr_pool_t *p)
742 const ap_slotmem_provider_t *storage = slotmem_shm_getstorage();
743 ap_register_provider(p, AP_SLOTMEM_PROVIDER_GROUP, "shm",
744 AP_SLOTMEM_PROVIDER_VERSION, storage);
745 ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_LAST);
746 ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE);
749 AP_DECLARE_MODULE(slotmem_shm) = {
750 STANDARD20_MODULE_STUFF,
751 NULL, /* create per-directory config structure */
752 NULL, /* merge per-directory config structures */
753 NULL, /* create per-server config structure */
754 NULL, /* merge per-server config structures */
755 NULL, /* command apr_table_t */
756 ap_slotmem_shm_register_hook /* register hooks */