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 #ifdef AP_NEED_SET_MUTEX_PERMS
32 #include <unistd.h> /* for getpid() */
45 #define AP_SLOTMEM_IS_PREGRAB(t) (t->desc.type & AP_SLOTMEM_TYPE_PREGRAB)
46 #define AP_SLOTMEM_IS_PERSIST(t) (t->desc.type & AP_SLOTMEM_TYPE_PERSIST)
48 /* The description of the slots to reuse the slotmem */
50 apr_size_t size; /* size of each memory slot */
51 unsigned int num; /* number of mem slots */
52 ap_slotmem_type_t type; /* type-specific flags */
55 #define AP_SLOTMEM_OFFSET (APR_ALIGN_DEFAULT(sizeof(sharedslotdesc_t)))
56 #define AP_UNSIGNEDINT_OFFSET (APR_ALIGN_DEFAULT(sizeof(unsigned int)))
58 struct ap_slotmem_instance_t {
59 char *name; /* per segment name */
60 void *shm; /* ptr to memory segment (apr_shm_t *) */
61 void *base; /* data set start */
62 apr_pool_t *gpool; /* per segment global pool */
63 char *inuse; /* in-use flag table*/
64 unsigned int *num_free; /* slot free count for this instance */
65 void *persist; /* persist dataset start */
66 sharedslotdesc_t desc; /* per slot desc */
67 struct ap_slotmem_instance_t *next; /* location of next allocated segment */
73 * sharedslotdesc_t | num_free | slots | isuse array |
76 * . persist (also num_free)
79 /* global pool and list of slotmem we are handling */
80 static struct ap_slotmem_instance_t *globallistmem = NULL;
81 static apr_pool_t *gpool = NULL;
83 /* apr:shmem/unix/shm.c */
84 static apr_status_t unixd_set_shm_perms(const char *fname)
86 #ifdef AP_NEED_SET_MUTEX_PERMS
87 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
88 struct shmid_ds shmbuf;
92 shmkey = ftok(fname, 1);
93 if (shmkey == (key_t)-1) {
96 if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
99 #if MODULE_MAGIC_NUMBER_MAJOR <= 20081212
100 #define ap_unixd_config unixd_config
102 shmbuf.shm_perm.uid = ap_unixd_config.user_id;
103 shmbuf.shm_perm.gid = ap_unixd_config.group_id;
104 shmbuf.shm_perm.mode = 0600;
105 if (shmctl(shmid, IPC_SET, &shmbuf) == -1) {
118 * Persist the slotmem in a file
119 * slotmem name and file name.
120 * none : no persistent data
121 * anonymous : $server_root/logs/anonymous.slotmem
122 * :rel_name : $server_root/logs/rel_name.slotmem
123 * abs_name : $abs_name.slotmem
126 static const char *store_filename(apr_pool_t *pool, const char *slotmemname)
128 const char *storename;
130 if (strcasecmp(slotmemname, "none") == 0)
132 else if (strcasecmp(slotmemname, "anonymous") == 0)
133 fname = ap_server_root_relative(pool, "logs/anonymous");
134 else if (slotmemname[0] == ':') {
136 tmpname = apr_pstrcat(pool, "logs/", &slotmemname[1], NULL);
137 fname = ap_server_root_relative(pool, tmpname);
142 storename = apr_pstrcat(pool, fname, ".slotmem", NULL);
146 static void store_slotmem(ap_slotmem_instance_t *slotmem)
151 const char *storename;
153 storename = store_filename(slotmem->gpool, slotmem->name);
156 rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE,
157 APR_OS_DEFAULT, slotmem->gpool);
158 if (APR_STATUS_IS_EEXIST(rv)) {
159 apr_file_remove(storename, slotmem->gpool);
160 rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE,
161 APR_OS_DEFAULT, slotmem->gpool);
163 if (rv != APR_SUCCESS) {
166 nbytes = (slotmem->desc.size * slotmem->desc.num) +
167 (slotmem->desc.num * sizeof(char)) + AP_UNSIGNEDINT_OFFSET;
168 apr_file_write(fp, slotmem->persist, &nbytes);
173 /* should be apr_status_t really */
174 static void restore_slotmem(void *ptr, const char *name, apr_size_t size,
177 const char *storename;
179 apr_size_t nbytes = size;
182 storename = store_filename(pool, name);
185 rv = apr_file_open(&fp, storename, APR_READ | APR_WRITE, APR_OS_DEFAULT,
187 if (rv == APR_SUCCESS) {
189 if (apr_file_info_get(&fi, APR_FINFO_SIZE, fp) == APR_SUCCESS) {
190 if (fi.size == nbytes) {
191 apr_file_read(fp, ptr, &nbytes);
195 apr_file_remove(storename, pool);
204 static apr_status_t cleanup_slotmem(void *param)
206 ap_slotmem_instance_t **mem = param;
209 ap_slotmem_instance_t *next = *mem;
210 apr_pool_t *p = next->gpool;
212 if (AP_SLOTMEM_IS_PERSIST(next)) {
215 apr_shm_destroy((apr_shm_t *)next->shm);
220 /* If shared mem was never called, then just remove
222 apr_pool_destroy(gpool);
227 static apr_status_t slotmem_doall(ap_slotmem_instance_t *mem,
228 ap_slotmem_callback_fn_t *func,
229 void *data, apr_pool_t *pool)
234 apr_status_t retval = APR_SUCCESS;
237 return APR_ENOSHMAVAIL;
240 ptr = (char *)mem->base;
242 for (i = 0; i < mem->desc.num; i++, inuse++) {
243 if (!AP_SLOTMEM_IS_PREGRAB(mem) ||
244 (AP_SLOTMEM_IS_PREGRAB(mem) && *inuse)) {
245 retval = func((void *) ptr, data, pool);
246 if (retval != APR_SUCCESS)
249 ptr += mem->desc.size;
254 static apr_status_t slotmem_create(ap_slotmem_instance_t **new,
255 const char *name, apr_size_t item_size,
256 unsigned int item_num,
257 ap_slotmem_type_t type, apr_pool_t *pool)
259 /* void *slotmem = NULL; */
261 sharedslotdesc_t desc;
262 ap_slotmem_instance_t *res;
263 ap_slotmem_instance_t *next = globallistmem;
266 apr_size_t basesize = (item_size * item_num);
267 apr_size_t size = AP_SLOTMEM_OFFSET + AP_UNSIGNEDINT_OFFSET +
268 (item_num * sizeof(char)) + basesize;
272 return APR_ENOSHMAVAIL;
274 if (name[0] == ':') {
278 fname = ap_server_root_relative(pool, name);
281 /* first try to attach to existing slotmem */
284 if (strcmp(next->name, fname) == 0) {
285 /* we already have it */
300 /* first try to attach to existing shared memory */
301 if (name && name[0] != ':') {
302 rv = apr_shm_attach(&shm, fname, gpool);
307 if (rv == APR_SUCCESS) {
309 if (apr_shm_size_get(shm) != size) {
313 ptr = (char *)apr_shm_baseaddr_get(shm);
314 memcpy(&desc, ptr, sizeof(desc));
315 if (desc.size != item_size || desc.num != item_num) {
319 ptr += AP_SLOTMEM_OFFSET;
322 apr_size_t dsize = size - AP_SLOTMEM_OFFSET;
323 if (name && name[0] != ':') {
324 apr_shm_remove(fname, gpool);
325 rv = apr_shm_create(&shm, size, fname, gpool);
328 rv = apr_shm_create(&shm, size, NULL, gpool);
330 if (rv != APR_SUCCESS) {
333 if (name && name[0] != ':') {
334 /* Set permissions to shared memory
335 * so it can be attached by child process
336 * having different user credentials
338 * See apr:shmem/unix/shm.c
340 unixd_set_shm_perms(fname);
342 ptr = (char *)apr_shm_baseaddr_get(shm);
343 desc.size = item_size;
346 memcpy(ptr, &desc, sizeof(desc));
347 ptr += AP_SLOTMEM_OFFSET;
348 memset(ptr, 0, dsize);
350 * TODO: Error check the below... What error makes
351 * sense if the restore fails? Any?
353 if (type & AP_SLOTMEM_TYPE_PERSIST) {
354 restore_slotmem(ptr, fname, dsize, pool);
358 /* For the chained slotmem stuff */
359 res = (ap_slotmem_instance_t *) apr_pcalloc(gpool,
360 sizeof(ap_slotmem_instance_t));
361 res->name = apr_pstrdup(gpool, fname);
363 res->num_free = (unsigned int *)ptr;
364 *res->num_free = item_num;
365 res->persist = (void *)ptr;
366 ptr += AP_UNSIGNEDINT_OFFSET;
367 res->base = (void *)ptr;
371 res->inuse = ptr + basesize;
372 if (globallistmem == NULL) {
383 static apr_status_t slotmem_attach(ap_slotmem_instance_t **new,
384 const char *name, apr_size_t *item_size,
385 unsigned int *item_num, apr_pool_t *pool)
387 /* void *slotmem = NULL; */
389 ap_slotmem_instance_t *res;
390 ap_slotmem_instance_t *next = globallistmem;
391 sharedslotdesc_t desc;
397 return APR_ENOSHMAVAIL;
400 if (name[0] == ':') {
404 fname = ap_server_root_relative(pool, name);
408 return APR_ENOSHMAVAIL;
411 /* first try to attach to existing slotmem */
414 if (strcmp(next->name, fname) == 0) {
415 /* we already have it */
417 *item_size = next->desc.size;
418 *item_num = next->desc.num;
428 /* first try to attach to existing shared memory */
429 rv = apr_shm_attach(&shm, fname, gpool);
430 if (rv != APR_SUCCESS) {
434 /* Read the description of the slotmem */
435 ptr = (char *)apr_shm_baseaddr_get(shm);
436 memcpy(&desc, ptr, sizeof(desc));
437 ptr += AP_SLOTMEM_OFFSET;
439 /* For the chained slotmem stuff */
440 res = (ap_slotmem_instance_t *) apr_pcalloc(gpool,
441 sizeof(ap_slotmem_instance_t));
442 res->name = apr_pstrdup(gpool, fname);
444 res->num_free = (unsigned int *)ptr;
445 res->persist = (void *)ptr;
446 ptr += AP_UNSIGNEDINT_OFFSET;
447 res->base = (void *)ptr;
450 res->inuse = ptr + (desc.size * desc.num);
452 if (globallistmem == NULL) {
460 *item_size = desc.size;
461 *item_num = desc.num;
465 static apr_status_t slotmem_dptr(ap_slotmem_instance_t *slot,
466 unsigned int id, void **mem)
471 return APR_ENOSHMAVAIL;
473 if (id >= slot->desc.num) {
474 return APR_ENOSHMAVAIL;
477 ptr = (char *)slot->base + slot->desc.size * id;
479 return APR_ENOSHMAVAIL;
485 static apr_status_t slotmem_get(ap_slotmem_instance_t *slot, unsigned int id,
486 unsigned char *dest, apr_size_t dest_len)
493 return APR_ENOSHMAVAIL;
496 inuse = slot->inuse + id;
497 if (id >= slot->desc.num || (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse)) {
500 ret = slotmem_dptr(slot, id, &ptr);
501 if (ret != APR_SUCCESS) {
505 memcpy(dest, ptr, dest_len); /* bounds check? */
509 static apr_status_t slotmem_put(ap_slotmem_instance_t *slot, unsigned int id,
510 unsigned char *src, apr_size_t src_len)
517 return APR_ENOSHMAVAIL;
520 inuse = slot->inuse + id;
521 if (id >= slot->desc.num || (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse)) {
524 ret = slotmem_dptr(slot, id, &ptr);
525 if (ret != APR_SUCCESS) {
529 memcpy(ptr, src, src_len); /* bounds check? */
533 static unsigned int slotmem_num_slots(ap_slotmem_instance_t *slot)
535 return slot->desc.num;
538 static unsigned int slotmem_num_free_slots(ap_slotmem_instance_t *slot)
540 if (AP_SLOTMEM_IS_PREGRAB(slot))
541 return *slot->num_free;
543 unsigned int i, counter=0;
544 char *inuse = slot->inuse;
545 for (i=0; i<slot->desc.num; i++, inuse++) {
553 static apr_size_t slotmem_slot_size(ap_slotmem_instance_t *slot)
555 return slot->desc.size;
558 static apr_status_t slotmem_grab(ap_slotmem_instance_t *slot, unsigned int *id)
564 return APR_ENOSHMAVAIL;
569 for (i = 0; i < slot->desc.num; i++, inuse++) {
574 if (i >= slot->desc.num) {
575 return APR_ENOSHMAVAIL;
583 static apr_status_t slotmem_release(ap_slotmem_instance_t *slot,
589 return APR_ENOSHMAVAIL;
594 if (id >= slot->desc.num || !inuse[id] ) {
602 static const ap_slotmem_provider_t storage = {
611 &slotmem_num_free_slots,
617 /* make the storage usuable from outside */
618 static const ap_slotmem_provider_t *slotmem_shm_getstorage(void)
623 /* initialise the global pool */
624 static void slotmem_shm_initgpool(apr_pool_t *p)
629 /* Add the pool_clean routine */
630 static void slotmem_shm_initialize_cleanup(apr_pool_t *p)
632 apr_pool_cleanup_register(p, &globallistmem, cleanup_slotmem,
633 apr_pool_cleanup_null);
637 * Make sure the shared memory is cleaned
639 static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
642 slotmem_shm_initialize_cleanup(p);
646 static int pre_config(apr_pool_t *p, apr_pool_t *plog,
649 apr_pool_t *global_pool;
652 rv = apr_pool_create(&global_pool, NULL);
653 if (rv != APR_SUCCESS) {
654 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
655 "Fatal error: unable to create global pool for shared slotmem");
658 slotmem_shm_initgpool(global_pool);
662 static void ap_slotmem_shm_register_hook(apr_pool_t *p)
664 const ap_slotmem_provider_t *storage = slotmem_shm_getstorage();
665 ap_register_provider(p, AP_SLOTMEM_PROVIDER_GROUP, "shared", "0", storage);
666 ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_LAST);
667 ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE);
670 AP_DECLARE_MODULE(slotmem_shm) = {
671 STANDARD20_MODULE_STUFF,
672 NULL, /* create per-directory config structure */
673 NULL, /* merge per-directory config structures */
674 NULL, /* create per-server config structure */
675 NULL, /* merge per-server config structures */
676 NULL, /* command apr_table_t */
677 ap_slotmem_shm_register_hook /* register hooks */