]> granicus.if.org Git - apache/blob - modules/slotmem/mod_slotmem_shm.c
Add inuse table and grab/release to plainmem slot provider
[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 #define AP_SLOTMEM_IS_PREGRAB(t) (t->type & AP_SLOTMEM_TYPE_PREGRAB)
43
44 struct ap_slotmem_instance_t {
45     char                 *name;       /* per segment name */
46     void                 *shm;        /* ptr to memory segment (apr_shm_t *) */
47     void                 *base;       /* data set start */
48     apr_size_t           size;        /* size of each memory slot */
49     unsigned int         num;         /* number of mem slots */
50     apr_pool_t           *gpool;      /* per segment global pool */
51     char                 *inuse;      /* in-use flag table*/
52     ap_slotmem_type_t    type;        /* type-specific flags */
53     struct ap_slotmem_instance_t  *next;       /* location of next allocated segment */
54 };
55
56
57 /* The description of the slots to reuse the slotmem */
58 struct sharedslotdesc {
59     apr_size_t item_size;
60     unsigned int item_num;
61     ap_slotmem_type_t type;
62 };
63
64 /*
65  * Memory layout:
66  *     sharedslotdesc | slots | isuse array
67  */
68
69 /* global pool and list of slotmem we are handling */
70 static struct ap_slotmem_instance_t *globallistmem = NULL;
71 static apr_pool_t *gpool = NULL;
72
73 /* apr:shmem/unix/shm.c */
74 static apr_status_t unixd_set_shm_perms(const char *fname)
75 {
76 #ifdef AP_NEED_SET_MUTEX_PERMS
77 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
78     struct shmid_ds shmbuf;
79     key_t shmkey;
80     int shmid;
81
82     shmkey = ftok(fname, 1);
83     if (shmkey == (key_t)-1) {
84         return errno;
85     }
86     if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
87         return errno;
88     }
89 #if MODULE_MAGIC_NUMBER_MAJOR > 20081212
90     shmbuf.shm_perm.uid  = ap_unixd_config.user_id;
91     shmbuf.shm_perm.gid  = ap_unixd_config.group_id;
92 #else
93     shmbuf.shm_perm.uid  = unixd_config.user_id;
94     shmbuf.shm_perm.gid  = unixd_config.group_id;
95 #endif
96     shmbuf.shm_perm.mode = 0600;
97     if (shmctl(shmid, IPC_SET, &shmbuf) == -1) {
98         return errno;
99     }
100     return APR_SUCCESS;
101 #else
102     return APR_ENOTIMPL;
103 #endif
104 #else
105     return APR_ENOTIMPL;
106 #endif
107 }
108
109 /*
110  * Persiste the slotmem in a file
111  * slotmem name and file name.
112  * anonymous : $server_root/logs/anonymous.slotmem
113  * :module.c : $server_root/logs/module.c.slotmem
114  * abs_name  : $abs_name.slotmem
115  *
116  */
117 static const char *store_filename(apr_pool_t *pool, const char *slotmemname)
118 {
119     const char *storename;
120     const char *fname;
121     if (strcmp(slotmemname, "anonymous") == 0)
122         fname = ap_server_root_relative(pool, "logs/anonymous");
123     else if (slotmemname[0] == ':') {
124         const char *tmpname;
125         tmpname = apr_pstrcat(pool, "logs/", &slotmemname[1], NULL);
126         fname = ap_server_root_relative(pool, tmpname);
127     }
128     else {
129         fname = slotmemname;
130     }
131     storename = apr_pstrcat(pool, fname, ".slotmem", NULL);
132     return storename;
133 }
134
135 static void store_slotmem(ap_slotmem_instance_t *slotmem)
136 {
137     apr_file_t *fp;
138     apr_status_t rv;
139     apr_size_t nbytes;
140     const char *storename;
141
142     storename = store_filename(slotmem->gpool, slotmem->name);
143
144     rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE, APR_OS_DEFAULT, slotmem->gpool);
145     if (APR_STATUS_IS_EEXIST(rv)) {
146         apr_file_remove(storename, slotmem->gpool);
147         rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE, APR_OS_DEFAULT, slotmem->gpool);
148     }
149     if (rv != APR_SUCCESS) {
150         return;
151     }
152     nbytes = (slotmem->size * slotmem->num) + (slotmem->num * sizeof(char));
153     apr_file_write(fp, slotmem->base, &nbytes);
154     apr_file_close(fp);
155 }
156
157 static void restore_slotmem(void *ptr, const char *name, apr_size_t size, apr_pool_t *pool)
158 {
159     const char *storename;
160     apr_file_t *fp;
161     apr_size_t nbytes = size;
162     apr_status_t rv;
163
164     storename = store_filename(pool, name);
165     rv = apr_file_open(&fp, storename, APR_READ | APR_WRITE, APR_OS_DEFAULT, pool);
166     if (rv == APR_SUCCESS) {
167         apr_finfo_t fi;
168         if (apr_file_info_get(&fi, APR_FINFO_SIZE, fp) == APR_SUCCESS) {
169             if (fi.size == nbytes) {
170                 apr_file_read(fp, ptr, &nbytes);
171             }
172             else {
173                 apr_file_close(fp);
174                 apr_file_remove(storename, pool);
175                 return;
176             }
177         }
178         apr_file_close(fp);
179     }
180 }
181
182 static apr_status_t cleanup_slotmem(void *param)
183 {
184     ap_slotmem_instance_t **mem = param;
185     apr_status_t rv;
186     apr_pool_t *pool = NULL;
187
188     if (*mem) {
189         ap_slotmem_instance_t *next = *mem;
190         pool = next->gpool;
191         while (next) {
192             store_slotmem(next);
193             rv = apr_shm_destroy((apr_shm_t *)next->shm);
194             next = next->next;
195         }
196         apr_pool_destroy(pool);
197     }
198     return APR_SUCCESS;
199 }
200
201 static apr_status_t slotmem_doall(ap_slotmem_instance_t *mem, ap_slotmem_callback_fn_t *func, void *data, apr_pool_t *pool)
202 {
203     unsigned int i;
204     void *ptr;
205     char *inuse;
206
207     if (!mem) {
208         return APR_ENOSHMAVAIL;
209     }
210
211     ptr = mem->base;
212     inuse = mem->inuse;
213     for (i = 0; i < mem->num; i++, inuse++) {
214         if (!AP_SLOTMEM_IS_PREGRAB(mem) ||
215            (AP_SLOTMEM_IS_PREGRAB(mem) && *inuse)) {
216             func((void *) ptr, data, pool);
217         }
218         ptr += mem->size;
219     }
220     return APR_SUCCESS;
221 }
222
223 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)
224 {
225 /*    void *slotmem = NULL; */
226     void *ptr;
227     struct sharedslotdesc desc;
228     ap_slotmem_instance_t *res;
229     ap_slotmem_instance_t *next = globallistmem;
230     const char *fname;
231     apr_shm_t *shm;
232     apr_size_t basesize = (item_size * item_num);
233     apr_size_t size = sizeof(struct sharedslotdesc) + (item_num * sizeof(char)) + basesize;
234     apr_status_t rv;
235
236     if (gpool == NULL)
237         return APR_ENOSHMAVAIL;
238     if (name) {
239         if (name[0] == ':') {
240             fname = name;
241         }
242         else {
243             fname = ap_server_root_relative(pool, name);
244         }
245
246         /* first try to attach to existing slotmem */
247         while (next) {
248             if (strcmp(next->name, fname) == 0) {
249                 /* we already have it */
250                 *new = next;
251                 return APR_SUCCESS;
252             }
253             next = next->next;
254         }
255     }
256     else {
257         fname = "anonymous";
258     }
259
260     /* first try to attach to existing shared memory */
261     if (name && name[0] != ':') {
262         rv = apr_shm_attach(&shm, fname, gpool);
263     }
264     else {
265         rv = APR_EINVAL;
266     }
267     if (rv == APR_SUCCESS) {
268         /* check size */
269         if (apr_shm_size_get(shm) != size) {
270             apr_shm_detach(shm);
271             return APR_EINVAL;
272         }
273         ptr = apr_shm_baseaddr_get(shm);
274         memcpy(&desc, ptr, sizeof(desc));
275         if (desc.item_size != item_size || desc.item_num != item_num) {
276             apr_shm_detach(shm);
277             return APR_EINVAL;
278         }
279         ptr = ptr + sizeof(desc);
280     }
281     else {
282         apr_size_t dsize = size - sizeof(struct sharedslotdesc);
283         if (name && name[0] != ':') {
284             apr_shm_remove(fname, gpool);
285             rv = apr_shm_create(&shm, size, fname, gpool);
286         }
287         else {
288             rv = apr_shm_create(&shm, size, NULL, gpool);
289         }
290         if (rv != APR_SUCCESS) {
291             return rv;
292         }
293         if (name && name[0] != ':') {
294             /* Set permissions to shared memory
295              * so it can be attached by child process
296              * having different user credentials
297              *
298              * See apr:shmem/unix/shm.c
299              */
300             unixd_set_shm_perms(fname);
301         }
302         ptr = apr_shm_baseaddr_get(shm);
303         desc.item_size = item_size;
304         desc.item_num = item_num;
305         desc.type = type;
306         memcpy(ptr, &desc, sizeof(desc));
307         ptr = ptr + sizeof(desc);
308         memset(ptr, 0, dsize);
309         if (type & AP_SLOTMEM_TYPE_PERSIST)
310             restore_slotmem(ptr, fname, dsize, pool);
311     }
312
313     /* For the chained slotmem stuff */
314     res = (ap_slotmem_instance_t *) apr_pcalloc(gpool, sizeof(ap_slotmem_instance_t));
315     res->name = apr_pstrdup(gpool, fname);
316     res->shm = shm;
317     res->base = ptr;
318     res->size = item_size;
319     res->num = item_num;
320     res->type = type;
321     res->gpool = gpool;
322     res->next = NULL;
323     res->inuse = ptr + basesize;
324     if (globallistmem == NULL) {
325         globallistmem = res;
326     }
327     else {
328         next->next = res;
329     }
330
331     *new = res;
332     return APR_SUCCESS;
333 }
334
335 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)
336 {
337 /*    void *slotmem = NULL; */
338     void *ptr;
339     ap_slotmem_instance_t *res;
340     ap_slotmem_instance_t *next = globallistmem;
341     struct sharedslotdesc desc;
342     const char *fname;
343     apr_shm_t *shm;
344     apr_status_t rv;
345
346     if (gpool == NULL) {
347         return APR_ENOSHMAVAIL;
348     }
349     if (name) {
350         if (name[0] == ':') {
351             fname = name;
352         }
353         else {
354             fname = ap_server_root_relative(pool, name);
355         }
356     }
357     else {
358         return APR_ENOSHMAVAIL;
359     }
360
361     /* first try to attach to existing slotmem */
362     while (next) {
363         if (strcmp(next->name, fname) == 0) {
364             /* we already have it */
365             *new = next;
366             *item_size = next->size;
367             *item_num = next->num;
368             return APR_SUCCESS;
369         }
370         next = next->next;
371     }
372
373     /* first try to attach to existing shared memory */
374     rv = apr_shm_attach(&shm, fname, gpool);
375     if (rv != APR_SUCCESS) {
376         return rv;
377     }
378
379     /* Read the description of the slotmem */
380     ptr = apr_shm_baseaddr_get(shm);
381     memcpy(&desc, ptr, sizeof(desc));
382     ptr = ptr + sizeof(desc);
383
384     /* For the chained slotmem stuff */
385     res = (ap_slotmem_instance_t *) apr_pcalloc(gpool, sizeof(ap_slotmem_instance_t));
386     res->name = apr_pstrdup(gpool, fname);
387     res->shm = shm;
388     res->base = ptr;
389     res->size = desc.item_size;
390     res->num = desc.item_num;
391     res->type = desc.type;
392     res->gpool = gpool;
393     res->inuse = ptr + (desc.item_size * desc.item_num);
394     res->next = NULL;
395     if (globallistmem == NULL) {
396         globallistmem = res;
397     }
398     else {
399         next->next = res;
400     }
401
402     *new = res;
403     *item_size = desc.item_size;
404     *item_num = desc.item_num;
405     return APR_SUCCESS;
406 }
407
408 static apr_status_t slotmem_dptr(ap_slotmem_instance_t *slot, unsigned int id, void **mem)
409 {
410     void *ptr;
411
412     if (!slot) {
413         return APR_ENOSHMAVAIL;
414     }
415     if (id < 0 || id >= slot->num) {
416         return APR_ENOSHMAVAIL;
417     }
418
419     ptr = slot->base + slot->size * id;
420     if (!ptr) {
421         return APR_ENOSHMAVAIL;
422     }
423     *mem = ptr;
424     return APR_SUCCESS;
425 }
426
427 static apr_status_t slotmem_get(ap_slotmem_instance_t *slot, unsigned int id, unsigned char *dest, apr_size_t dest_len)
428 {
429     void *ptr;
430     char *inuse;
431     apr_status_t ret;
432
433     if (!slot) {
434         return APR_ENOSHMAVAIL;
435     }
436
437     inuse = slot->inuse + id;
438     if (id >= slot->num || (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse)) {
439         return APR_NOTFOUND;
440     }
441     ret = slotmem_dptr(slot, id, &ptr);
442     if (ret != APR_SUCCESS) {
443         return ret;
444     }
445     memcpy(dest, ptr, dest_len); /* bounds check? */
446     return APR_SUCCESS;
447 }
448
449 static apr_status_t slotmem_put(ap_slotmem_instance_t *slot, unsigned int id, unsigned char *src, apr_size_t src_len)
450 {
451     void *ptr;
452     char *inuse;
453     apr_status_t ret;
454
455     if (!slot) {
456         return APR_ENOSHMAVAIL;
457     }
458
459     inuse = slot->inuse + id;
460     if (id >= slot->num || (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse)) {
461         return APR_NOTFOUND;
462     }
463     ret = slotmem_dptr(slot, id, &ptr);
464     if (ret != APR_SUCCESS) {
465         return ret;
466     }
467     memcpy(ptr, src, src_len); /* bounds check? */
468     return APR_SUCCESS;
469 }
470
471 static unsigned int slotmem_num_slots(ap_slotmem_instance_t *slot)
472 {
473     return slot->num;
474 }
475
476 static apr_size_t slotmem_slot_size(ap_slotmem_instance_t *slot)
477 {
478     return slot->size;
479 }
480
481 /*
482  * XXXX: if !AP_SLOTMEM_IS_PREGRAB, then still worry about
483  *       inuse for grab and return?
484  */
485 static apr_status_t slotmem_grab(ap_slotmem_instance_t *slot, unsigned int *id)
486 {
487     unsigned int i;
488     char *inuse;
489
490     if (!slot) {
491         return APR_ENOSHMAVAIL;
492     }
493
494     inuse = slot->inuse;
495
496     for (i = 0; i < slot->num; i++, inuse++) {
497         if (!*inuse) {
498             break;
499         }
500     }
501     if (i >= slot->num) {
502         return APR_ENOSHMAVAIL;
503     }
504     *inuse = 1;
505     *id = i;
506     return APR_SUCCESS;
507 }
508
509 static apr_status_t slotmem_release(ap_slotmem_instance_t *slot, unsigned int id)
510 {
511     char *inuse;
512
513     if (!slot) {
514         return APR_ENOSHMAVAIL;
515     }
516
517     inuse = slot->inuse;
518
519     if (id >= slot->num || !inuse[id] ) {
520         return APR_NOTFOUND;
521     }
522     inuse[id] = 0;
523     return APR_SUCCESS;
524 }
525
526 static const ap_slotmem_provider_t storage = {
527     "sharedmem",
528     &slotmem_doall,
529     &slotmem_create,
530     &slotmem_attach,
531     &slotmem_dptr,
532     &slotmem_get,
533     &slotmem_put,
534     &slotmem_num_slots,
535     &slotmem_slot_size
536 };
537
538 /* make the storage usuable from outside */
539 static const ap_slotmem_provider_t *slotmem_shm_getstorage(void)
540 {
541     return (&storage);
542 }
543
544 /* initialise the global pool */
545 static void slotmem_shm_initgpool(apr_pool_t *p)
546 {
547     gpool = p;
548 }
549
550 /* Add the pool_clean routine */
551 static void slotmem_shm_initialize_cleanup(apr_pool_t *p)
552 {
553     apr_pool_cleanup_register(p, &globallistmem, cleanup_slotmem, apr_pool_cleanup_null);
554 }
555
556 /*
557  * Make sure the shared memory is cleaned
558  */
559 static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
560 {
561     void *data;
562     const char *userdata_key = "slotmem_shm_post_config";
563
564     apr_pool_userdata_get(&data, userdata_key, s->process->pool);
565     if (!data) {
566         apr_pool_userdata_set((const void *)1, userdata_key,
567                                apr_pool_cleanup_null, s->process->pool);
568         return OK;
569     }
570
571     slotmem_shm_initialize_cleanup(p);
572     return OK;
573 }
574
575 static int pre_config(apr_pool_t *p, apr_pool_t *plog,
576                       apr_pool_t *ptemp)
577 {
578     apr_pool_t *global_pool;
579     apr_status_t rv;
580
581     rv = apr_pool_create(&global_pool, NULL);
582     if (rv != APR_SUCCESS) {
583         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
584             "Fatal error: unable to create global pool for shared slotmem");
585         return rv;
586     }
587     slotmem_shm_initgpool(global_pool);
588     return OK;
589 }
590
591 static void ap_slotmem_shm_register_hook(apr_pool_t *p)
592 {
593     const ap_slotmem_provider_t *storage = slotmem_shm_getstorage();
594     ap_register_provider(p, AP_SLOTMEM_PROVIDER_GROUP, "shared", "0", storage);
595     ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_LAST);
596     ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE);
597 }
598
599 module AP_MODULE_DECLARE_DATA slotmem_shm_module = {
600     STANDARD20_MODULE_STUFF,
601     NULL,                       /* create per-directory config structure */
602     NULL,                       /* merge per-directory config structures */
603     NULL,                       /* create per-server config structure */
604     NULL,                       /* merge per-server config structures */
605     NULL,                       /* command apr_table_t */
606     ap_slotmem_shm_register_hook  /* register hooks */
607 };
608