]> granicus.if.org Git - apache/blob - modules/slotmem/mod_slotmem_shm.c
Use ap_state_query() to fix many modules that were not correctly initializing
[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  * Shared memory is cleaned-up for each restart, graceful or
21  * otherwise.
22  */
23
24 #include  "ap_slotmem.h"
25
26 #include "httpd.h"
27 #ifdef AP_NEED_SET_MUTEX_PERMS
28 #include "unixd.h"
29 #endif
30
31 #if APR_HAVE_UNISTD_H
32 #include <unistd.h>         /* for getpid() */
33 #endif
34
35 #if HAVE_SYS_SEM_H
36 #include <sys/shm.h>
37 #if !defined(SHM_R)
38 #define SHM_R 0400
39 #endif
40 #if !defined(SHM_W)
41 #define SHM_W 0200
42 #endif
43 #endif
44
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)
47
48 /* The description of the slots to reuse the slotmem */
49 typedef struct {
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 */
53 } sharedslotdesc_t;
54
55 #define AP_SLOTMEM_OFFSET (APR_ALIGN_DEFAULT(sizeof(sharedslotdesc_t)))
56 #define AP_UNSIGNEDINT_OFFSET (APR_ALIGN_DEFAULT(sizeof(unsigned int)))
57
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 */
68 };
69
70
71 /*
72  * Memory layout:
73  *     sharedslotdesc_t | num_free | slots | isuse array |
74  *                      ^          ^
75  *                      |          . base
76  *                      . persist (also num_free)
77  */
78
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;
82
83 /* apr:shmem/unix/shm.c */
84 static apr_status_t unixd_set_shm_perms(const char *fname)
85 {
86 #ifdef AP_NEED_SET_MUTEX_PERMS
87 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
88     struct shmid_ds shmbuf;
89     key_t shmkey;
90     int shmid;
91
92     shmkey = ftok(fname, 1);
93     if (shmkey == (key_t)-1) {
94         return errno;
95     }
96     if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
97         return errno;
98     }
99 #if MODULE_MAGIC_NUMBER_MAJOR <= 20081212
100 #define ap_unixd_config unixd_config
101 #endif
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) {
106         return errno;
107     }
108     return APR_SUCCESS;
109 #else
110     return APR_ENOTIMPL;
111 #endif
112 #else
113     return APR_ENOTIMPL;
114 #endif
115 }
116
117 /*
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
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 (strcasecmp(slotmemname, "none") == 0)
131         return NULL;
132     else if (strcasecmp(slotmemname, "anonymous") == 0)
133         fname = ap_server_root_relative(pool, "logs/anonymous");
134     else if (slotmemname[0] == ':') {
135         const char *tmpname;
136         tmpname = apr_pstrcat(pool, "logs/", &slotmemname[1], NULL);
137         fname = ap_server_root_relative(pool, tmpname);
138     }
139     else {
140         fname = slotmemname;
141     }
142     storename = apr_pstrcat(pool, fname, ".slotmem", NULL);
143     return storename;
144 }
145
146 static void store_slotmem(ap_slotmem_instance_t *slotmem)
147 {
148     apr_file_t *fp;
149     apr_status_t rv;
150     apr_size_t nbytes;
151     const char *storename;
152
153     storename = store_filename(slotmem->gpool, slotmem->name);
154
155     if (storename) {
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);
162         }
163         if (rv != APR_SUCCESS) {
164             return;
165         }
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);
169         apr_file_close(fp);
170     }
171 }
172
173 /* should be apr_status_t really */
174 static void restore_slotmem(void *ptr, const char *name, apr_size_t size,
175                             apr_pool_t *pool)
176 {
177     const char *storename;
178     apr_file_t *fp;
179     apr_size_t nbytes = size;
180     apr_status_t rv;
181
182     storename = store_filename(pool, name);
183
184     if (storename) {
185         rv = apr_file_open(&fp, storename, APR_READ | APR_WRITE, APR_OS_DEFAULT,
186                            pool);
187         if (rv == APR_SUCCESS) {
188             apr_finfo_t fi;
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);
192                 }
193                 else {
194                     apr_file_close(fp);
195                     apr_file_remove(storename, pool);
196                     return;
197                 }
198             }
199             apr_file_close(fp);
200         }
201     }
202 }
203
204 static apr_status_t cleanup_slotmem(void *param)
205 {
206     ap_slotmem_instance_t **mem = param;
207
208     if (*mem) {
209         ap_slotmem_instance_t *next = *mem;
210         apr_pool_t *p = next->gpool;
211         while (next) {
212             if (AP_SLOTMEM_IS_PERSIST(next)) {
213                 store_slotmem(next);
214             }
215             apr_shm_destroy((apr_shm_t *)next->shm);
216             next = next->next;
217         }
218         apr_pool_destroy(p);
219     } else {
220         /* If shared mem was never called, then just remove
221          * the global pool */
222         apr_pool_destroy(gpool);
223     }
224     return APR_SUCCESS;
225 }
226
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)
230 {
231     unsigned int i;
232     char *ptr;
233     char *inuse;
234     apr_status_t retval = APR_SUCCESS;
235
236     if (!mem) {
237         return APR_ENOSHMAVAIL;
238     }
239
240     ptr = (char *)mem->base;
241     inuse = mem->inuse;
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)
247                 break;
248         }
249         ptr += mem->desc.size;
250     }
251     return retval;
252 }
253
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)
258 {
259 /*    void *slotmem = NULL; */
260     char *ptr;
261     sharedslotdesc_t desc;
262     ap_slotmem_instance_t *res;
263     ap_slotmem_instance_t *next = globallistmem;
264     const char *fname;
265     apr_shm_t *shm;
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;
269     apr_status_t rv;
270
271     if (gpool == NULL)
272         return APR_ENOSHMAVAIL;
273     if (name) {
274         if (name[0] == ':') {
275             fname = name;
276         }
277         else {
278             fname = ap_server_root_relative(pool, name);
279         }
280
281         /* first try to attach to existing slotmem */
282         if (next) {
283             for (;;) {
284                 if (strcmp(next->name, fname) == 0) {
285                     /* we already have it */
286                     *new = next;
287                     return APR_SUCCESS;
288                 }
289                 if (!next->next) {
290                      break;
291                 }
292                 next = next->next;
293             }
294         }
295     }
296     else {
297         fname = "anonymous";
298     }
299
300     /* first try to attach to existing shared memory */
301     if (name && name[0] != ':') {
302         rv = apr_shm_attach(&shm, fname, gpool);
303     }
304     else {
305         rv = APR_EINVAL;
306     }
307     if (rv == APR_SUCCESS) {
308         /* check size */
309         if (apr_shm_size_get(shm) != size) {
310             apr_shm_detach(shm);
311             return APR_EINVAL;
312         }
313         ptr = (char *)apr_shm_baseaddr_get(shm);
314         memcpy(&desc, ptr, sizeof(desc));
315         if (desc.size != item_size || desc.num != item_num) {
316             apr_shm_detach(shm);
317             return APR_EINVAL;
318         }
319         ptr += AP_SLOTMEM_OFFSET;
320     }
321     else {
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);
326         }
327         else {
328             rv = apr_shm_create(&shm, size, NULL, gpool);
329         }
330         if (rv != APR_SUCCESS) {
331             return rv;
332         }
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
337              *
338              * See apr:shmem/unix/shm.c
339              */
340             unixd_set_shm_perms(fname);
341         }
342         ptr = (char *)apr_shm_baseaddr_get(shm);
343         desc.size = item_size;
344         desc.num = item_num;
345         desc.type = type;
346         memcpy(ptr, &desc, sizeof(desc));
347         ptr += AP_SLOTMEM_OFFSET;
348         memset(ptr, 0, dsize);
349         /*
350          * TODO: Error check the below... What error makes
351          * sense if the restore fails? Any?
352          */
353         if (type & AP_SLOTMEM_TYPE_PERSIST) {
354             restore_slotmem(ptr, fname, dsize, pool);
355         }
356     }
357
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);
362     res->shm = shm;
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;
368     res->desc = desc;
369     res->gpool = gpool;
370     res->next = NULL;
371     res->inuse = ptr + basesize;
372     if (globallistmem == NULL) {
373         globallistmem = res;
374     }
375     else {
376         next->next = res;
377     }
378
379     *new = res;
380     return APR_SUCCESS;
381 }
382
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)
386 {
387 /*    void *slotmem = NULL; */
388     char *ptr;
389     ap_slotmem_instance_t *res;
390     ap_slotmem_instance_t *next = globallistmem;
391     sharedslotdesc_t desc;
392     const char *fname;
393     apr_shm_t *shm;
394     apr_status_t rv;
395
396     if (gpool == NULL) {
397         return APR_ENOSHMAVAIL;
398     }
399     if (name) {
400         if (name[0] == ':') {
401             fname = name;
402         }
403         else {
404             fname = ap_server_root_relative(pool, name);
405         }
406     }
407     else {
408         return APR_ENOSHMAVAIL;
409     }
410
411     /* first try to attach to existing slotmem */
412     if (next) {
413         for (;;) {
414             if (strcmp(next->name, fname) == 0) {
415                 /* we already have it */
416                 *new = next;
417                 *item_size = next->desc.size;
418                 *item_num = next->desc.num;
419                 return APR_SUCCESS;
420             }
421             if (!next->next) {
422                  break;
423             }
424             next = next->next;
425         }
426     }
427
428     /* first try to attach to existing shared memory */
429     rv = apr_shm_attach(&shm, fname, gpool);
430     if (rv != APR_SUCCESS) {
431         return rv;
432     }
433
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;
438
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);
443     res->shm = shm;
444     res->num_free = (unsigned int *)ptr;
445     res->persist = (void *)ptr;
446     ptr += AP_UNSIGNEDINT_OFFSET;
447     res->base = (void *)ptr;
448     res->desc = desc;
449     res->gpool = gpool;
450     res->inuse = ptr + (desc.size * desc.num);
451     res->next = NULL;
452     if (globallistmem == NULL) {
453         globallistmem = res;
454     }
455     else {
456         next->next = res;
457     }
458
459     *new = res;
460     *item_size = desc.size;
461     *item_num = desc.num;
462     return APR_SUCCESS;
463 }
464
465 static apr_status_t slotmem_dptr(ap_slotmem_instance_t *slot,
466                                  unsigned int id, void **mem)
467 {
468     char *ptr;
469
470     if (!slot) {
471         return APR_ENOSHMAVAIL;
472     }
473     if (id >= slot->desc.num) {
474         return APR_ENOSHMAVAIL;
475     }
476
477     ptr = (char *)slot->base + slot->desc.size * id;
478     if (!ptr) {
479         return APR_ENOSHMAVAIL;
480     }
481     *mem = (void *)ptr;
482     return APR_SUCCESS;
483 }
484
485 static apr_status_t slotmem_get(ap_slotmem_instance_t *slot, unsigned int id,
486                                 unsigned char *dest, apr_size_t dest_len)
487 {
488     void *ptr;
489     char *inuse;
490     apr_status_t ret;
491
492     if (!slot) {
493         return APR_ENOSHMAVAIL;
494     }
495
496     inuse = slot->inuse + id;
497     if (id >= slot->desc.num || (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse)) {
498         return APR_NOTFOUND;
499     }
500     ret = slotmem_dptr(slot, id, &ptr);
501     if (ret != APR_SUCCESS) {
502         return ret;
503     }
504     *inuse = 1;
505     memcpy(dest, ptr, dest_len); /* bounds check? */
506     return APR_SUCCESS;
507 }
508
509 static apr_status_t slotmem_put(ap_slotmem_instance_t *slot, unsigned int id,
510                                 unsigned char *src, apr_size_t src_len)
511 {
512     void *ptr;
513     char *inuse;
514     apr_status_t ret;
515
516     if (!slot) {
517         return APR_ENOSHMAVAIL;
518     }
519
520     inuse = slot->inuse + id;
521     if (id >= slot->desc.num || (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse)) {
522         return APR_NOTFOUND;
523     }
524     ret = slotmem_dptr(slot, id, &ptr);
525     if (ret != APR_SUCCESS) {
526         return ret;
527     }
528     *inuse=1;
529     memcpy(ptr, src, src_len); /* bounds check? */
530     return APR_SUCCESS;
531 }
532
533 static unsigned int slotmem_num_slots(ap_slotmem_instance_t *slot)
534 {
535     return slot->desc.num;
536 }
537
538 static unsigned int slotmem_num_free_slots(ap_slotmem_instance_t *slot)
539 {
540     if (AP_SLOTMEM_IS_PREGRAB(slot))
541         return *slot->num_free;
542     else {
543         unsigned int i, counter=0;
544         char *inuse = slot->inuse;
545         for (i=0; i<slot->desc.num; i++, inuse++) {
546             if (!*inuse)
547                 counter++;
548         }
549         return counter;
550     }
551 }
552
553 static apr_size_t slotmem_slot_size(ap_slotmem_instance_t *slot)
554 {
555     return slot->desc.size;
556 }
557
558 static apr_status_t slotmem_grab(ap_slotmem_instance_t *slot, unsigned int *id)
559 {
560     unsigned int i;
561     char *inuse;
562
563     if (!slot) {
564         return APR_ENOSHMAVAIL;
565     }
566
567     inuse = slot->inuse;
568
569     for (i = 0; i < slot->desc.num; i++, inuse++) {
570         if (!*inuse) {
571             break;
572         }
573     }
574     if (i >= slot->desc.num) {
575         return APR_ENOSHMAVAIL;
576     }
577     *inuse = 1;
578     *id = i;
579     (*slot->num_free)--;
580     return APR_SUCCESS;
581 }
582
583 static apr_status_t slotmem_release(ap_slotmem_instance_t *slot,
584                                     unsigned int id)
585 {
586     char *inuse;
587
588     if (!slot) {
589         return APR_ENOSHMAVAIL;
590     }
591
592     inuse = slot->inuse;
593
594     if (id >= slot->desc.num || !inuse[id] ) {
595         return APR_NOTFOUND;
596     }
597     inuse[id] = 0;
598     (*slot->num_free)++;
599     return APR_SUCCESS;
600 }
601
602 static const ap_slotmem_provider_t storage = {
603     "sharedmem",
604     &slotmem_doall,
605     &slotmem_create,
606     &slotmem_attach,
607     &slotmem_dptr,
608     &slotmem_get,
609     &slotmem_put,
610     &slotmem_num_slots,
611     &slotmem_num_free_slots,
612     &slotmem_slot_size,
613     &slotmem_grab,
614     &slotmem_release
615 };
616
617 /* make the storage usuable from outside */
618 static const ap_slotmem_provider_t *slotmem_shm_getstorage(void)
619 {
620     return (&storage);
621 }
622
623 /* initialise the global pool */
624 static void slotmem_shm_initgpool(apr_pool_t *p)
625 {
626     gpool = p;
627 }
628
629 /* Add the pool_clean routine */
630 static void slotmem_shm_initialize_cleanup(apr_pool_t *p)
631 {
632     apr_pool_cleanup_register(p, &globallistmem, cleanup_slotmem,
633                               apr_pool_cleanup_null);
634 }
635
636 /*
637  * Make sure the shared memory is cleaned
638  */
639 static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
640                        server_rec *s)
641 {
642     slotmem_shm_initialize_cleanup(p);
643     return OK;
644 }
645
646 static int pre_config(apr_pool_t *p, apr_pool_t *plog,
647                       apr_pool_t *ptemp)
648 {
649     apr_pool_t *global_pool;
650     apr_status_t rv;
651
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");
656         return rv;
657     }
658     slotmem_shm_initgpool(global_pool);
659     return OK;
660 }
661
662 static void ap_slotmem_shm_register_hook(apr_pool_t *p)
663 {
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);
668 }
669
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 */
678 };
679