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.
21 #include "ap_slotmem.h"
24 #ifdef AP_NEED_SET_MUTEX_PERMS
29 #include <unistd.h> /* for getpid() */
42 struct ap_slotmem_instance_t {
43 char *name; /* per segment name */
44 void *shm; /* ptr to memory segment (apr_shm_t *) */
45 void *base; /* data set start */
46 apr_size_t size; /* size of each memory slot */
47 unsigned int num; /* number of mem slots */
48 apr_pool_t *gpool; /* per segment global pool */
49 apr_global_mutex_t *smutex; /* mutex */
50 struct ap_slotmem_instance_t *next; /* location of next allocated segment */
51 char *inuse; /* in-use flag table*/
55 /* The description of the slots to reuse the slotmem */
56 struct sharedslotdesc {
58 unsigned int item_num;
63 * sharedslotdesc | slots | isuse array
66 /* global pool and list of slotmem we are handling */
67 static struct ap_slotmem_instance_t *globallistmem = NULL;
68 static apr_pool_t *gpool = NULL;
69 static apr_global_mutex_t *smutex = NULL;
70 static const char *mutex_fname = NULL;
72 #define SLOTMEM_LOCK(s) do { \
74 apr_global_mutex_lock(s); \
77 #define SLOTMEM_UNLOCK(s) do { \
79 apr_global_mutex_unlock(s); \
82 /* apr:shmem/unix/shm.c */
83 static apr_status_t unixd_set_shm_perms(const char *fname)
85 #ifdef AP_NEED_SET_MUTEX_PERMS
86 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
87 struct shmid_ds shmbuf;
91 shmkey = ftok(fname, 1);
92 if (shmkey == (key_t)-1) {
95 if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
98 #if MODULE_MAGIC_NUMBER_MAJOR > 20081212
99 shmbuf.shm_perm.uid = ap_unixd_config.user_id;
100 shmbuf.shm_perm.gid = ap_unixd_config.group_id;
102 shmbuf.shm_perm.uid = unixd_config.user_id;
103 shmbuf.shm_perm.gid = unixd_config.group_id;
105 shmbuf.shm_perm.mode = 0600;
106 if (shmctl(shmid, IPC_SET, &shmbuf) == -1) {
119 * Persiste the slotmem in a file
120 * slotmem name and file name.
121 * anonymous : $server_root/logs/anonymous.slotmem
122 * :module.c : $server_root/logs/module.c.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 (strcmp(slotmemname, "anonymous") == 0)
131 fname = ap_server_root_relative(pool, "logs/anonymous");
132 else if (slotmemname[0] == ':') {
134 tmpname = apr_pstrcat(pool, "logs/", &slotmemname[1], NULL);
135 fname = ap_server_root_relative(pool, tmpname);
140 storename = apr_pstrcat(pool, fname, ".slotmem", NULL);
144 static void store_slotmem(ap_slotmem_instance_t *slotmem)
149 const char *storename;
151 storename = store_filename(slotmem->gpool, slotmem->name);
153 rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE, APR_OS_DEFAULT, slotmem->gpool);
154 if (APR_STATUS_IS_EEXIST(rv)) {
155 apr_file_remove(storename, slotmem->gpool);
156 rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE, APR_OS_DEFAULT, slotmem->gpool);
158 if (rv != APR_SUCCESS) {
161 nbytes = (slotmem->size * slotmem->num) + (slotmem->num * sizeof(char));
162 apr_file_write(fp, slotmem->base, &nbytes);
166 static void restore_slotmem(void *ptr, const char *name, apr_size_t size, apr_pool_t *pool)
168 const char *storename;
170 apr_size_t nbytes = size;
173 storename = store_filename(pool, name);
174 rv = apr_file_open(&fp, storename, APR_READ | APR_WRITE, APR_OS_DEFAULT, pool);
175 if (rv == APR_SUCCESS) {
177 if (apr_file_info_get(&fi, APR_FINFO_SIZE, fp) == APR_SUCCESS) {
178 if (fi.size == nbytes) {
179 apr_file_read(fp, ptr, &nbytes);
183 apr_file_remove(storename, pool);
191 static apr_status_t cleanup_slotmem(void *param)
193 ap_slotmem_instance_t **mem = param;
195 apr_pool_t *pool = NULL;
198 ap_slotmem_instance_t *next = *mem;
202 rv = apr_shm_destroy((apr_shm_t *)next->shm);
205 apr_pool_destroy(pool);
210 static apr_status_t slotmem_do(ap_slotmem_instance_t *mem, ap_slotmem_callback_fn_t *func, void *data, apr_pool_t *pool)
217 return APR_ENOSHMAVAIL;
222 SLOTMEM_LOCK(mem->smutex);
223 for (i = 0; i < mem->num; i++, inuse++) {
225 func((void *) ptr, data, pool);
229 SLOTMEM_UNLOCK(mem->smutex);
233 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)
235 /* void *slotmem = NULL; */
237 struct sharedslotdesc desc;
238 ap_slotmem_instance_t *res;
239 ap_slotmem_instance_t *next = globallistmem;
242 apr_size_t basesize = (item_size * item_num);
243 apr_size_t size = sizeof(struct sharedslotdesc) + (item_num * sizeof(char)) + basesize;
247 return APR_ENOSHMAVAIL;
249 if (name[0] == ':') {
253 fname = ap_server_root_relative(pool, name);
256 /* first try to attach to existing slotmem */
258 if (strcmp(next->name, fname) == 0) {
259 /* we already have it */
270 /* first try to attach to existing shared memory */
271 if (name && name[0] != ':') {
272 rv = apr_shm_attach(&shm, fname, gpool);
277 if (rv == APR_SUCCESS) {
279 if (apr_shm_size_get(shm) != size) {
283 ptr = apr_shm_baseaddr_get(shm);
284 memcpy(&desc, ptr, sizeof(desc));
285 if (desc.item_size != item_size || desc.item_num != item_num) {
289 ptr = ptr + sizeof(desc);
292 apr_size_t dsize = size - sizeof(struct sharedslotdesc);
293 SLOTMEM_LOCK(smutex);
294 if (name && name[0] != ':') {
295 apr_shm_remove(fname, gpool);
296 rv = apr_shm_create(&shm, size, fname, gpool);
299 rv = apr_shm_create(&shm, size, NULL, gpool);
301 SLOTMEM_UNLOCK(smutex);
302 if (rv != APR_SUCCESS) {
305 if (name && name[0] != ':') {
306 /* Set permissions to shared memory
307 * so it can be attached by child process
308 * having different user credentials
310 * See apr:shmem/unix/shm.c
312 unixd_set_shm_perms(fname);
314 ptr = apr_shm_baseaddr_get(shm);
315 desc.item_size = item_size;
316 desc.item_num = item_num;
317 memcpy(ptr, &desc, sizeof(desc));
318 ptr = ptr + sizeof(desc);
319 memset(ptr, 0, dsize);
320 if (type & AP_SLOTMEM_TYPE_PERSIST)
321 restore_slotmem(ptr, fname, dsize, pool);
324 /* For the chained slotmem stuff */
325 res = (ap_slotmem_instance_t *) apr_pcalloc(gpool, sizeof(ap_slotmem_instance_t));
326 res->name = apr_pstrdup(gpool, fname);
329 res->size = item_size;
332 res->smutex = smutex;
334 res->inuse = ptr + basesize;
335 if (globallistmem == NULL) {
346 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)
348 /* void *slotmem = NULL; */
350 ap_slotmem_instance_t *res;
351 ap_slotmem_instance_t *next = globallistmem;
352 struct sharedslotdesc desc;
358 return APR_ENOSHMAVAIL;
361 if (name[0] == ':') {
365 fname = ap_server_root_relative(pool, name);
369 return APR_ENOSHMAVAIL;
372 /* first try to attach to existing slotmem */
374 if (strcmp(next->name, fname) == 0) {
375 /* we already have it */
377 *item_size = next->size;
378 *item_num = next->num;
384 /* first try to attach to existing shared memory */
385 rv = apr_shm_attach(&shm, fname, gpool);
386 if (rv != APR_SUCCESS) {
390 /* Read the description of the slotmem */
391 ptr = apr_shm_baseaddr_get(shm);
392 memcpy(&desc, ptr, sizeof(desc));
393 ptr = ptr + sizeof(desc);
395 /* For the chained slotmem stuff */
396 res = (ap_slotmem_instance_t *) apr_pcalloc(gpool, sizeof(ap_slotmem_instance_t));
397 res->name = apr_pstrdup(gpool, fname);
400 res->size = desc.item_size;
401 res->num = desc.item_num;
403 res->smutex = smutex;
404 res->inuse = ptr + (desc.item_size * desc.item_num);
406 if (globallistmem == NULL) {
414 *item_size = desc.item_size;
415 *item_num = desc.item_num;
419 static apr_status_t slotmem_mem(ap_slotmem_instance_t *slot, unsigned int id, void **mem)
425 return APR_ENOSHMAVAIL;
427 if (id < 0 || id >= slot->num) {
428 return APR_ENOSHMAVAIL;
431 ptr = slot->base + slot->size * id;
433 return APR_ENOSHMAVAIL;
439 static apr_status_t slotmem_get(ap_slotmem_instance_t *slot, unsigned int id, unsigned char *dest, apr_size_t dest_len)
447 return APR_ENOSHMAVAIL;
451 if (id >= slot->num || !inuse[id] ) {
454 ret = slotmem_mem(slot, id, &ptr);
455 if (ret != APR_SUCCESS) {
458 memcpy(dest, ptr, dest_len); /* bounds check? */
462 static apr_status_t slotmem_put(ap_slotmem_instance_t *slot, unsigned int id, unsigned char *src, apr_size_t src_len)
470 return APR_ENOSHMAVAIL;
474 if (id >= slot->num || !inuse[id] ) {
477 ret = slotmem_mem(slot, id, &ptr);
478 if (ret != APR_SUCCESS) {
481 memcpy(ptr, src, src_len); /* bounds check? */
485 static unsigned int slotmem_num_slots(ap_slotmem_instance_t *slot)
490 static apr_size_t slotmem_slot_size(ap_slotmem_instance_t *slot)
495 static apr_status_t slotmem_grab(ap_slotmem_instance_t *slot, unsigned int *id)
502 return APR_ENOSHMAVAIL;
507 SLOTMEM_LOCK(slot->smutex);
508 for (i = 0; i < slot->num; i++, inuse++) {
513 if (i >= slot->num) {
514 SLOTMEM_UNLOCK(slot->smutex);
515 return APR_ENOSHMAVAIL;
519 SLOTMEM_UNLOCK(slot->smutex);
523 static apr_status_t slotmem_return(ap_slotmem_instance_t *slot, unsigned int id)
529 return APR_ENOSHMAVAIL;
534 SLOTMEM_LOCK(slot->smutex);
535 if (id >= slot->num || !inuse[id] ) {
536 SLOTMEM_UNLOCK(slot->smutex);
540 SLOTMEM_UNLOCK(slot->smutex);
544 static const ap_slotmem_provider_t storage = {
556 /* make the storage usuable from outside */
557 static const ap_slotmem_provider_t *slotmem_shm_getstorage(void)
562 /* initialise the global pool */
563 static void slotmem_shm_initgpool(apr_pool_t *p)
568 /* Add the pool_clean routine */
569 static void slotmem_shm_initialize_cleanup(apr_pool_t *p)
571 apr_pool_cleanup_register(p, &globallistmem, cleanup_slotmem, apr_pool_cleanup_null);
575 * Create the shared mem mutex and
576 * make sure the shared memory is cleaned
578 static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
580 const char *temp_dir;
585 const char *userdata_key = "slotmem_shm_post_config";
587 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
589 apr_pool_userdata_set((const void *)1, userdata_key,
590 apr_pool_cleanup_null, s->process->pool);
594 rv = apr_temp_dir_get(&temp_dir, p);
595 if (rv != APR_SUCCESS) {
596 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
597 "slotmem_shm: search for temporary directory failed");
600 apr_filepath_merge(&template, temp_dir, "slotmem_shm.lck.XXXXXX",
601 APR_FILEPATH_NATIVE, p);
602 rv = apr_file_mktemp(&fmutex, template, 0, p);
603 if (rv != APR_SUCCESS) {
604 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
605 "slotmem_shm: creation of mutex file in directory %s failed",
610 rv = apr_file_name_get(&mutex_fname, fmutex);
611 if (rv != APR_SUCCESS) {
612 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
613 "slotmem_shm: unable to get mutex fname");
617 rv = apr_file_close(fmutex);
618 if (rv != APR_SUCCESS) {
619 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
620 "slotmem_shm: could not close mutex file");
624 rv = apr_global_mutex_create(&smutex,
625 mutex_fname, APR_LOCK_DEFAULT, p);
626 if (rv != APR_SUCCESS) {
627 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
628 "slotmem_shm: creation of mutex failed");
632 #ifdef AP_NEED_SET_MUTEX_PERMS
633 rv = ap_unixd_set_global_mutex_perms(smutex);
634 if (rv != APR_SUCCESS) {
635 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
636 "slotmem_shm: failed to set mutex permissions");
641 slotmem_shm_initialize_cleanup(p);
645 static int pre_config(apr_pool_t *p, apr_pool_t *plog,
648 apr_pool_t *global_pool;
651 rv = apr_pool_create(&global_pool, NULL);
652 if (rv != APR_SUCCESS) {
653 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
654 "Fatal error: unable to create global pool for shared slotmem");
657 slotmem_shm_initgpool(global_pool);
661 static void child_init(apr_pool_t *p, server_rec *s)
665 rv = apr_global_mutex_child_init(&smutex,
667 if (rv != APR_SUCCESS) {
668 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
669 "Failed to initialise global mutex %s in child process %"
671 mutex_fname, getpid());
675 static void ap_slotmem_shm_register_hook(apr_pool_t *p)
677 const ap_slotmem_provider_t *storage = slotmem_shm_getstorage();
678 ap_register_provider(p, AP_SLOTMEM_PROVIDER_GROUP, "shared", "0", storage);
679 ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_LAST);
680 ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE);
681 ap_hook_child_init(child_init, NULL, NULL, APR_HOOK_MIDDLE);
684 module AP_MODULE_DECLARE_DATA slotmem_shm_module = {
685 STANDARD20_MODULE_STUFF,
686 NULL, /* create per-directory config structure */
687 NULL, /* merge per-directory config structures */
688 NULL, /* create per-server config structure */
689 NULL, /* merge per-server config structures */
690 NULL, /* command apr_table_t */
691 ap_slotmem_shm_register_hook /* register hooks */