]> granicus.if.org Git - apache/blob - modules/slotmem/mod_slotmem_shm.c
Align with module naming
[apache] / modules / slotmem / mod_slotmem_shm.c
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /* Memory handler for a shared memory divided in slot.
18  * This one uses shared memory.
19  */
20
21 #include  "ap_slotmem.h"
22
23 #include "httpd.h"
24 #ifdef AP_NEED_SET_MUTEX_PERMS
25 #include "unixd.h"
26 #endif
27
28 #if APR_HAVE_UNISTD_H
29 #include <unistd.h>         /* for getpid() */
30 #endif
31
32 #if HAVE_SYS_SEM_H
33 #include <sys/shm.h>
34 #if !defined(SHM_R)
35 #define SHM_R 0400
36 #endif
37 #if !defined(SHM_W)
38 #define SHM_W 0200
39 #endif
40 #endif
41
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*/
52 };
53
54
55 /* The description of the slots to reuse the slotmem */
56 struct sharedslotdesc {
57     apr_size_t item_size;
58     unsigned int item_num;
59 };
60
61 /*
62  * Memory layout:
63  *     sharedslotdesc | slots | isuse array
64  */
65
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;
71
72 #define SLOTMEM_LOCK(s) do {      \
73     if (s)                        \
74         apr_global_mutex_lock(s); \
75 } while (0)
76
77 #define SLOTMEM_UNLOCK(s) do {      \
78     if (s)                          \
79         apr_global_mutex_unlock(s); \
80 } while (0)
81
82 /* apr:shmem/unix/shm.c */
83 static apr_status_t unixd_set_shm_perms(const char *fname)
84 {
85 #ifdef AP_NEED_SET_MUTEX_PERMS
86 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
87     struct shmid_ds shmbuf;
88     key_t shmkey;
89     int shmid;
90
91     shmkey = ftok(fname, 1);
92     if (shmkey == (key_t)-1) {
93         return errno;
94     }
95     if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
96         return errno;
97     }
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;
101 #else
102     shmbuf.shm_perm.uid  = unixd_config.user_id;
103     shmbuf.shm_perm.gid  = unixd_config.group_id;
104 #endif
105     shmbuf.shm_perm.mode = 0600;
106     if (shmctl(shmid, IPC_SET, &shmbuf) == -1) {
107         return errno;
108     }
109     return APR_SUCCESS;
110 #else
111     return APR_ENOTIMPL;
112 #endif
113 #else
114     return APR_ENOTIMPL;
115 #endif
116 }
117
118 /*
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
124  *
125  */
126 static const char *store_filename(apr_pool_t *pool, const char *slotmemname)
127 {
128     const char *storename;
129     const char *fname;
130     if (strcmp(slotmemname, "anonymous") == 0)
131         fname = ap_server_root_relative(pool, "logs/anonymous");
132     else if (slotmemname[0] == ':') {
133         const char *tmpname;
134         tmpname = apr_pstrcat(pool, "logs/", &slotmemname[1], NULL);
135         fname = ap_server_root_relative(pool, tmpname);
136     }
137     else {
138         fname = slotmemname;
139     }
140     storename = apr_pstrcat(pool, fname, ".slotmem", NULL);
141     return storename;
142 }
143
144 static void store_slotmem(ap_slotmem_instance_t *slotmem)
145 {
146     apr_file_t *fp;
147     apr_status_t rv;
148     apr_size_t nbytes;
149     const char *storename;
150
151     storename = store_filename(slotmem->gpool, slotmem->name);
152
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);
157     }
158     if (rv != APR_SUCCESS) {
159         return;
160     }
161     nbytes = (slotmem->size * slotmem->num) + (slotmem->num * sizeof(char));
162     apr_file_write(fp, slotmem->base, &nbytes);
163     apr_file_close(fp);
164 }
165
166 static void restore_slotmem(void *ptr, const char *name, apr_size_t size, apr_pool_t *pool)
167 {
168     const char *storename;
169     apr_file_t *fp;
170     apr_size_t nbytes = size;
171     apr_status_t rv;
172
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) {
176         apr_finfo_t fi;
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);
180             }
181             else {
182                 apr_file_close(fp);
183                 apr_file_remove(storename, pool);
184                 return;
185             }
186         }
187         apr_file_close(fp);
188     }
189 }
190
191 static apr_status_t cleanup_slotmem(void *param)
192 {
193     ap_slotmem_instance_t **mem = param;
194     apr_status_t rv;
195     apr_pool_t *pool = NULL;
196
197     if (*mem) {
198         ap_slotmem_instance_t *next = *mem;
199         pool = next->gpool;
200         while (next) {
201             store_slotmem(next);
202             rv = apr_shm_destroy((apr_shm_t *)next->shm);
203             next = next->next;
204         }
205         apr_pool_destroy(pool);
206     }
207     return APR_SUCCESS;
208 }
209
210 static apr_status_t slotmem_do(ap_slotmem_instance_t *mem, ap_slotmem_callback_fn_t *func, void *data, apr_pool_t *pool)
211 {
212     unsigned int i;
213     void *ptr;
214     char *inuse;
215
216     if (!mem) {
217         return APR_ENOSHMAVAIL;
218     }
219
220     ptr = mem->base;
221     inuse = mem->inuse;
222     SLOTMEM_LOCK(mem->smutex);
223     for (i = 0; i < mem->num; i++, inuse++) {
224         if (*inuse) {
225             func((void *) ptr, data, pool);
226         }
227         ptr += mem->size;
228     }
229     SLOTMEM_UNLOCK(mem->smutex);
230     return APR_SUCCESS;
231 }
232
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)
234 {
235 /*    void *slotmem = NULL; */
236     void *ptr;
237     struct sharedslotdesc desc;
238     ap_slotmem_instance_t *res;
239     ap_slotmem_instance_t *next = globallistmem;
240     const char *fname;
241     apr_shm_t *shm;
242     apr_size_t basesize = (item_size * item_num);
243     apr_size_t size = sizeof(struct sharedslotdesc) + (item_num * sizeof(char)) + basesize;
244     apr_status_t rv;
245
246     if (gpool == NULL)
247         return APR_ENOSHMAVAIL;
248     if (name) {
249         if (name[0] == ':') {
250             fname = name;
251         }
252         else {
253             fname = ap_server_root_relative(pool, name);
254         }
255
256         /* first try to attach to existing slotmem */
257         while (next) {
258             if (strcmp(next->name, fname) == 0) {
259                 /* we already have it */
260                 *new = next;
261                 return APR_SUCCESS;
262             }
263             next = next->next;
264         }
265     }
266     else {
267         fname = "anonymous";
268     }
269
270     /* first try to attach to existing shared memory */
271     if (name && name[0] != ':') {
272         rv = apr_shm_attach(&shm, fname, gpool);
273     }
274     else {
275         rv = APR_EINVAL;
276     }
277     if (rv == APR_SUCCESS) {
278         /* check size */
279         if (apr_shm_size_get(shm) != size) {
280             apr_shm_detach(shm);
281             return APR_EINVAL;
282         }
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) {
286             apr_shm_detach(shm);
287             return APR_EINVAL;
288         }
289         ptr = ptr + sizeof(desc);
290     }
291     else {
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);
297         }
298         else {
299             rv = apr_shm_create(&shm, size, NULL, gpool);
300         }
301         SLOTMEM_UNLOCK(smutex);
302         if (rv != APR_SUCCESS) {
303             return rv;
304         }
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
309              *
310              * See apr:shmem/unix/shm.c
311              */
312             unixd_set_shm_perms(fname);
313         }
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);
322     }
323
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);
327     res->shm = shm;
328     res->base = ptr;
329     res->size = item_size;
330     res->num = item_num;
331     res->gpool = gpool;
332     res->smutex = smutex;
333     res->next = NULL;
334     res->inuse = ptr + basesize;
335     if (globallistmem == NULL) {
336         globallistmem = res;
337     }
338     else {
339         next->next = res;
340     }
341
342     *new = res;
343     return APR_SUCCESS;
344 }
345
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)
347 {
348 /*    void *slotmem = NULL; */
349     void *ptr;
350     ap_slotmem_instance_t *res;
351     ap_slotmem_instance_t *next = globallistmem;
352     struct sharedslotdesc desc;
353     const char *fname;
354     apr_shm_t *shm;
355     apr_status_t rv;
356
357     if (gpool == NULL) {
358         return APR_ENOSHMAVAIL;
359     }
360     if (name) {
361         if (name[0] == ':') {
362             fname = name;
363         }
364         else {
365             fname = ap_server_root_relative(pool, name);
366         }
367     }
368     else {
369         return APR_ENOSHMAVAIL;
370     }
371
372     /* first try to attach to existing slotmem */
373     while (next) {
374         if (strcmp(next->name, fname) == 0) {
375             /* we already have it */
376             *new = next;
377             *item_size = next->size;
378             *item_num = next->num;
379             return APR_SUCCESS;
380         }
381         next = next->next;
382     }
383
384     /* first try to attach to existing shared memory */
385     rv = apr_shm_attach(&shm, fname, gpool);
386     if (rv != APR_SUCCESS) {
387         return rv;
388     }
389
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);
394
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);
398     res->shm = shm;
399     res->base = ptr;
400     res->size = desc.item_size;
401     res->num = desc.item_num;
402     res->gpool = gpool;
403     res->smutex = smutex;
404     res->inuse = ptr + (desc.item_size * desc.item_num);
405     res->next = NULL;
406     if (globallistmem == NULL) {
407         globallistmem = res;
408     }
409     else {
410         next->next = res;
411     }
412
413     *new = res;
414     *item_size = desc.item_size;
415     *item_num = desc.item_num;
416     return APR_SUCCESS;
417 }
418
419 static apr_status_t slotmem_mem(ap_slotmem_instance_t *slot, unsigned int id, void **mem)
420 {
421
422     void *ptr;
423
424     if (!slot) {
425         return APR_ENOSHMAVAIL;
426     }
427     if (id < 0 || id >= slot->num) {
428         return APR_ENOSHMAVAIL;
429     }
430
431     ptr = slot->base + slot->size * id;
432     if (!ptr) {
433         return APR_ENOSHMAVAIL;
434     }
435     *mem = ptr;
436     return APR_SUCCESS;
437 }
438
439 static apr_status_t slotmem_get(ap_slotmem_instance_t *slot, unsigned int id, unsigned char *dest, apr_size_t dest_len)
440 {
441
442     void *ptr;
443     char *inuse;
444     apr_status_t ret;
445
446     if (!slot) {
447         return APR_ENOSHMAVAIL;
448     }
449
450     inuse = slot->inuse;
451     if (id >= slot->num || !inuse[id] ) {
452         return APR_NOTFOUND;
453     }
454     ret = slotmem_mem(slot, id, &ptr);
455     if (ret != APR_SUCCESS) {
456         return ret;
457     }
458     memcpy(dest, ptr, dest_len); /* bounds check? */
459     return APR_SUCCESS;
460 }
461
462 static apr_status_t slotmem_put(ap_slotmem_instance_t *slot, unsigned int id, unsigned char *src, apr_size_t src_len)
463 {
464
465     void *ptr;
466     char *inuse;
467     apr_status_t ret;
468
469     if (!slot) {
470         return APR_ENOSHMAVAIL;
471     }
472
473     inuse = slot->inuse;
474     if (id >= slot->num || !inuse[id] ) {
475         return APR_NOTFOUND;
476     }
477     ret = slotmem_mem(slot, id, &ptr);
478     if (ret != APR_SUCCESS) {
479         return ret;
480     }
481     memcpy(ptr, src, src_len); /* bounds check? */
482     return APR_SUCCESS;
483 }
484
485 static unsigned int slotmem_num_slots(ap_slotmem_instance_t *slot)
486 {
487     return slot->num;
488 }
489
490 static apr_size_t slotmem_slot_size(ap_slotmem_instance_t *slot)
491 {
492     return slot->size;
493 }
494
495 static apr_status_t slotmem_grab(ap_slotmem_instance_t *slot, unsigned int *id)
496 {
497
498     unsigned int i;
499     char *inuse;
500
501     if (!slot) {
502         return APR_ENOSHMAVAIL;
503     }
504
505     inuse = slot->inuse;
506
507     SLOTMEM_LOCK(slot->smutex);
508     for (i = 0; i < slot->num; i++, inuse++) {
509         if (!*inuse) {
510             break;
511         }
512     }
513     if (i >= slot->num) {
514         SLOTMEM_UNLOCK(slot->smutex);
515         return APR_ENOSHMAVAIL;
516     }
517     *inuse = 1;
518     *id = i;
519     SLOTMEM_UNLOCK(slot->smutex);
520     return APR_SUCCESS;
521 }
522
523 static apr_status_t slotmem_return(ap_slotmem_instance_t *slot, unsigned int id)
524 {
525
526     char *inuse;
527
528     if (!slot) {
529         return APR_ENOSHMAVAIL;
530     }
531
532     inuse = slot->inuse;
533
534     SLOTMEM_LOCK(slot->smutex);
535     if (id >= slot->num || !inuse[id] ) {
536         SLOTMEM_UNLOCK(slot->smutex);
537         return APR_NOTFOUND;
538     }
539     inuse[id] = 0;
540     SLOTMEM_UNLOCK(slot->smutex);
541     return APR_SUCCESS;
542 }
543
544 static const ap_slotmem_provider_t storage = {
545     "sharedmem",
546     &slotmem_do,
547     &slotmem_create,
548     &slotmem_attach,
549     &slotmem_mem,
550     &slotmem_get,
551     &slotmem_put,
552     &slotmem_num_slots,
553     &slotmem_slot_size
554 };
555
556 /* make the storage usuable from outside */
557 static const ap_slotmem_provider_t *slotmem_shm_getstorage(void)
558 {
559     return (&storage);
560 }
561
562 /* initialise the global pool */
563 static void slotmem_shm_initgpool(apr_pool_t *p)
564 {
565     gpool = p;
566 }
567
568 /* Add the pool_clean routine */
569 static void slotmem_shm_initialize_cleanup(apr_pool_t *p)
570 {
571     apr_pool_cleanup_register(p, &globallistmem, cleanup_slotmem, apr_pool_cleanup_null);
572 }
573
574 /*
575  * Create the shared mem mutex and
576  * make sure the shared memory is cleaned
577  */
578 static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
579 {
580     const char *temp_dir;
581     char *template;
582     apr_status_t rv;
583     void *data;
584     apr_file_t *fmutex;
585     const char *userdata_key = "slotmem_shm_post_config";
586
587     apr_pool_userdata_get(&data, userdata_key, s->process->pool);
588     if (!data) {
589         apr_pool_userdata_set((const void *)1, userdata_key,
590                                apr_pool_cleanup_null, s->process->pool);
591         return OK;
592     }
593
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");
598         return rv;
599     }
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",
606                      temp_dir);
607         return rv;
608     }
609
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");
614         return rv;
615     }
616
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");
621         return rv;
622     }
623
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");
629         return rv;
630     }
631
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");
637         return rv;
638     }
639 #endif
640
641     slotmem_shm_initialize_cleanup(p);
642     return OK;
643 }
644
645 static int pre_config(apr_pool_t *p, apr_pool_t *plog,
646                       apr_pool_t *ptemp)
647 {
648     apr_pool_t *global_pool;
649     apr_status_t rv;
650
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");
655         return rv;
656     }
657     slotmem_shm_initgpool(global_pool);
658     return OK;
659 }
660
661 static void child_init(apr_pool_t *p, server_rec *s)
662 {
663     apr_status_t rv;
664
665     rv = apr_global_mutex_child_init(&smutex,
666                                      mutex_fname, p);
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 %"
670                      APR_PID_T_FMT ".",
671                      mutex_fname, getpid());
672     }
673 }
674
675 static void ap_slotmem_shm_register_hook(apr_pool_t *p)
676 {
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);
682 }
683
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 */
692 };
693