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; */
301 sharedslotdesc_t desc;
302 ap_slotmem_instance_t *res;
303 ap_slotmem_instance_t *next = globallistmem;
306 apr_size_t basesize = (item_size * item_num);
307 apr_size_t size = AP_SLOTMEM_OFFSET + AP_UNSIGNEDINT_OFFSET +
308 (item_num * sizeof(char)) + basesize;
312 return APR_ENOSHMAVAIL;
314 fname = slotmem_filename(pool, name, 0);
316 /* first try to attach to existing slotmem */
319 if (strcmp(next->name, fname) == 0) {
320 /* we already have it */
336 /* first try to attach to existing shared memory */
337 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02300)
338 "create %s: %"APR_SIZE_T_FMT"/%u", fname, item_size,
341 rv = apr_shm_attach(&shm, fname, gpool);
346 if (rv == APR_SUCCESS) {
348 if (apr_shm_size_get(shm) != size) {
352 ptr = (char *)apr_shm_baseaddr_get(shm);
353 memcpy(&desc, ptr, sizeof(desc));
354 if (desc.size != item_size || desc.num != item_num) {
358 ptr += AP_SLOTMEM_OFFSET;
361 apr_size_t dsize = size - AP_SLOTMEM_OFFSET;
363 apr_shm_remove(fname, gpool);
364 rv = apr_shm_create(&shm, size, fname, gpool);
367 rv = apr_shm_create(&shm, size, NULL, gpool);
369 if (rv != APR_SUCCESS) {
373 /* Set permissions to shared memory
374 * so it can be attached by child process
375 * having different user credentials
377 * See apr:shmem/unix/shm.c
379 unixd_set_shm_perms(fname);
381 ptr = (char *)apr_shm_baseaddr_get(shm);
382 desc.size = item_size;
385 memcpy(ptr, &desc, sizeof(desc));
386 ptr += AP_SLOTMEM_OFFSET;
387 memset(ptr, 0, dsize);
389 * TODO: Error check the below... What error makes
390 * sense if the restore fails? Any?
392 if (type & AP_SLOTMEM_TYPE_PERSIST) {
393 restore_slotmem(ptr, fname, dsize, pool);
398 /* For the chained slotmem stuff */
399 res = (ap_slotmem_instance_t *) apr_pcalloc(gpool,
400 sizeof(ap_slotmem_instance_t));
401 res->name = apr_pstrdup(gpool, fname);
402 res->fbased = fbased;
404 res->num_free = (unsigned int *)ptr;
406 *res->num_free = item_num;
408 res->persist = (void *)ptr;
409 ptr += AP_UNSIGNEDINT_OFFSET;
410 res->base = (void *)ptr;
414 res->inuse = ptr + basesize;
415 if (globallistmem == NULL) {
426 static apr_status_t slotmem_attach(ap_slotmem_instance_t **new,
427 const char *name, apr_size_t *item_size,
428 unsigned int *item_num, apr_pool_t *pool)
430 /* void *slotmem = NULL; */
432 ap_slotmem_instance_t *res;
433 ap_slotmem_instance_t *next = globallistmem;
434 sharedslotdesc_t desc;
440 return APR_ENOSHMAVAIL;
442 fname = slotmem_filename(pool, name, 0);
444 return APR_ENOSHMAVAIL;
447 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02301)
448 "attach looking for %s", fname);
450 /* first try to attach to existing slotmem */
453 if (strcmp(next->name, fname) == 0) {
454 /* we already have it */
456 *item_size = next->desc.size;
457 *item_num = next->desc.num;
458 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
460 "attach found %s: %"APR_SIZE_T_FMT"/%u", fname,
461 *item_size, *item_num);
471 /* next try to attach to existing shared memory */
472 rv = apr_shm_attach(&shm, fname, gpool);
473 if (rv != APR_SUCCESS) {
477 /* Read the description of the slotmem */
478 ptr = (char *)apr_shm_baseaddr_get(shm);
479 memcpy(&desc, ptr, sizeof(desc));
480 ptr += AP_SLOTMEM_OFFSET;
482 /* For the chained slotmem stuff */
483 res = (ap_slotmem_instance_t *) apr_pcalloc(gpool,
484 sizeof(ap_slotmem_instance_t));
485 res->name = apr_pstrdup(gpool, fname);
488 res->num_free = (unsigned int *)ptr;
489 res->persist = (void *)ptr;
490 ptr += AP_UNSIGNEDINT_OFFSET;
491 res->base = (void *)ptr;
494 res->inuse = ptr + (desc.size * desc.num);
496 if (globallistmem == NULL) {
504 *item_size = desc.size;
505 *item_num = desc.num;
506 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
508 "attach found %s: %"APR_SIZE_T_FMT"/%u", fname,
509 *item_size, *item_num);
513 static apr_status_t slotmem_dptr(ap_slotmem_instance_t *slot,
514 unsigned int id, void **mem)
519 return APR_ENOSHMAVAIL;
521 if (id >= slot->desc.num) {
525 ptr = (char *)slot->base + slot->desc.size * id;
527 return APR_ENOSHMAVAIL;
533 static apr_status_t slotmem_get(ap_slotmem_instance_t *slot, unsigned int id,
534 unsigned char *dest, apr_size_t dest_len)
541 return APR_ENOSHMAVAIL;
544 inuse = slot->inuse + id;
545 if (id >= slot->desc.num) {
548 if (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse) {
551 ret = slotmem_dptr(slot, id, &ptr);
552 if (ret != APR_SUCCESS) {
556 memcpy(dest, ptr, dest_len); /* bounds check? */
560 static apr_status_t slotmem_put(ap_slotmem_instance_t *slot, unsigned int id,
561 unsigned char *src, apr_size_t src_len)
568 return APR_ENOSHMAVAIL;
571 inuse = slot->inuse + id;
572 if (id >= slot->desc.num) {
575 if (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse) {
578 ret = slotmem_dptr(slot, id, &ptr);
579 if (ret != APR_SUCCESS) {
583 memcpy(ptr, src, src_len); /* bounds check? */
587 static unsigned int slotmem_num_slots(ap_slotmem_instance_t *slot)
589 return slot->desc.num;
592 static unsigned int slotmem_num_free_slots(ap_slotmem_instance_t *slot)
594 if (AP_SLOTMEM_IS_PREGRAB(slot))
595 return *slot->num_free;
597 unsigned int i, counter=0;
598 char *inuse = slot->inuse;
599 for (i=0; i<slot->desc.num; i++, inuse++) {
607 static apr_size_t slotmem_slot_size(ap_slotmem_instance_t *slot)
609 return slot->desc.size;
612 static apr_status_t slotmem_grab(ap_slotmem_instance_t *slot, unsigned int *id)
618 return APR_ENOSHMAVAIL;
623 for (i = 0; i < slot->desc.num; i++, inuse++) {
628 if (i >= slot->desc.num) {
629 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02293)
630 "slotmem(%s) grab failed. Num %u/num_free %u",
631 slot->name, slotmem_num_slots(slot),
632 slotmem_num_free_slots(slot));
641 static apr_status_t slotmem_fgrab(ap_slotmem_instance_t *slot, unsigned int id)
646 return APR_ENOSHMAVAIL;
649 if (id >= slot->desc.num) {
650 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02397)
651 "slotmem(%s) fgrab failed. Num %u/num_free %u",
652 slot->name, slotmem_num_slots(slot),
653 slotmem_num_free_slots(slot));
656 inuse = slot->inuse + id;
665 static apr_status_t slotmem_release(ap_slotmem_instance_t *slot,
671 return APR_ENOSHMAVAIL;
676 if (id >= slot->desc.num || !inuse[id] ) {
677 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02294)
678 "slotmem(%s) release failed. Num %u/inuse[%u] %d",
679 slot->name, slotmem_num_slots(slot),
681 if (id >= slot->desc.num) {
692 static const ap_slotmem_provider_t storage = {
701 &slotmem_num_free_slots,
708 /* make the storage usuable from outside */
709 static const ap_slotmem_provider_t *slotmem_shm_getstorage(void)
714 /* initialise the global pool */
715 static void slotmem_shm_initgpool(apr_pool_t *p)
720 /* Add the pool_clean routine */
721 static void slotmem_shm_initialize_cleanup(apr_pool_t *p)
723 apr_pool_cleanup_register(p, &globallistmem, cleanup_slotmem,
724 apr_pool_cleanup_null);
728 * Make sure the shared memory is cleaned
730 static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
733 slotmem_shm_initialize_cleanup(p);
737 static int pre_config(apr_pool_t *p, apr_pool_t *plog,
740 slotmem_shm_initgpool(p);
744 static void ap_slotmem_shm_register_hook(apr_pool_t *p)
746 const ap_slotmem_provider_t *storage = slotmem_shm_getstorage();
747 ap_register_provider(p, AP_SLOTMEM_PROVIDER_GROUP, "shm",
748 AP_SLOTMEM_PROVIDER_VERSION, storage);
749 ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_LAST);
750 ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE);
753 AP_DECLARE_MODULE(slotmem_shm) = {
754 STANDARD20_MODULE_STUFF,
755 NULL, /* create per-directory config structure */
756 NULL, /* merge per-directory config structures */
757 NULL, /* create per-server config structure */
758 NULL, /* merge per-server config structures */
759 NULL, /* command apr_table_t */
760 ap_slotmem_shm_register_hook /* register hooks */