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#include "http_main.h"
28#ifdef AP_NEED_SET_MUTEX_PERMS
29#include "unixd.h"
30#endif
31
32#if APR_HAVE_UNISTD_H
33#include <unistd.h>         /* for getpid() */
34#endif
35
36#if HAVE_SYS_SEM_H
37#include <sys/shm.h>
38#if !defined(SHM_R)
39#define SHM_R 0400
40#endif
41#if !defined(SHM_W)
42#define SHM_W 0200
43#endif
44#endif
45
46#define AP_SLOTMEM_IS_PREGRAB(t)    (t->desc.type & AP_SLOTMEM_TYPE_PREGRAB)
47#define AP_SLOTMEM_IS_PERSIST(t)    (t->desc.type & AP_SLOTMEM_TYPE_PERSIST)
48#define AP_SLOTMEM_IS_CLEARINUSE(t) (t->desc.type & AP_SLOTMEM_TYPE_CLEARINUSE)
49
50/* The description of the slots to reuse the slotmem */
51typedef struct {
52    apr_size_t size;             /* size of each memory slot */
53    unsigned int num;            /* number of mem slots */
54    ap_slotmem_type_t type;      /* type-specific flags */
55} sharedslotdesc_t;
56
57#define AP_SLOTMEM_OFFSET (APR_ALIGN_DEFAULT(sizeof(sharedslotdesc_t)))
58#define AP_UNSIGNEDINT_OFFSET (APR_ALIGN_DEFAULT(sizeof(unsigned int)))
59
60struct ap_slotmem_instance_t {
61    char                 *name;       /* per segment name */
62    int                  fbased;      /* filebased? */
63    void                 *shm;        /* ptr to memory segment (apr_shm_t *) */
64    void                 *base;       /* data set start */
65    apr_pool_t           *gpool;      /* per segment global pool */
66    char                 *inuse;      /* in-use flag table*/
67    unsigned int         *num_free;   /* slot free count for this instance */
68    void                 *persist;    /* persist dataset start */
69    sharedslotdesc_t     desc;        /* per slot desc */
70    struct ap_slotmem_instance_t  *next;       /* location of next allocated segment */
71};
72
73/*
74 * Memory layout:
75 *     sharedslotdesc_t | num_free | slots | isuse array |
76 *                      ^          ^
77 *                      |          . base
78 *                      . persist (also num_free)
79 */
80
81/* global pool and list of slotmem we are handling */
82static struct ap_slotmem_instance_t *globallistmem = NULL;
83static apr_pool_t *gpool = NULL;
84
85#define DEFAULT_SLOTMEM_PREFIX "slotmem-shm-"
86#define DEFAULT_SLOTMEM_SUFFIX ".shm"
87#define DEFAULT_SLOTMEM_PERSIST_SUFFIX ".persist"
88
89/* apr:shmem/unix/shm.c */
90static apr_status_t unixd_set_shm_perms(const char *fname)
91{
92#ifdef AP_NEED_SET_MUTEX_PERMS
93#if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
94    struct shmid_ds shmbuf = { { 0 } };
95    key_t shmkey;
96    int shmid;
97
98    shmkey = ftok(fname, 1);
99    if (shmkey == (key_t)-1) {
100        return errno;
101    }
102    if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
103        return errno;
104    }
105#if MODULE_MAGIC_NUMBER_MAJOR <= 20081212
106#define ap_unixd_config unixd_config
107#endif
108    shmbuf.shm_perm.uid  = ap_unixd_config.user_id;
109    shmbuf.shm_perm.gid  = ap_unixd_config.group_id;
110    shmbuf.shm_perm.mode = 0600;
111    if (shmctl(shmid, IPC_SET, &shmbuf) == -1) {
112        return errno;
113    }
114    return APR_SUCCESS;
115#else
116    return APR_ENOTIMPL;
117#endif
118#else
119    return APR_ENOTIMPL;
120#endif
121}
122
123/*
124 * Persist the slotmem in a file
125 * slotmem name and file name.
126 * none      : no persistent data
127 * rel_name  : $server_root/rel_name
128 * /abs_name : $abs_name
129 *
130 */
131
132static const char *slotmem_filename(apr_pool_t *pool, const char *slotmemname,
133                                    int persist)
134{
135    const char *fname;
136    if (!slotmemname || strcasecmp(slotmemname, "none") == 0) {
137        return NULL;
138    }
139    else if (slotmemname[0] != '/') {
140        const char *filenm = apr_pstrcat(pool, DEFAULT_SLOTMEM_PREFIX,
141                                         slotmemname, DEFAULT_SLOTMEM_SUFFIX,
142                                         NULL);
143        fname = ap_runtime_dir_relative(pool, filenm);
144    }
145    else {
146        fname = slotmemname;
147    }
148
149    if (persist) {
150        return apr_pstrcat(pool, fname, DEFAULT_SLOTMEM_PERSIST_SUFFIX,
151                           NULL);
152    }
153    return fname;
154}
155
156static void slotmem_clearinuse(ap_slotmem_instance_t *slot)
157{
158    unsigned int i;
159    char *inuse;
160
161    if (!slot) {
162        return;
163    }
164
165    inuse = slot->inuse;
166
167    for (i = 0; i < slot->desc.num; i++, inuse++) {
168        if (*inuse) {
169            *inuse = 0;
170            (*slot->num_free)++;
171        }
172    }
173}
174
175static void store_slotmem(ap_slotmem_instance_t *slotmem)
176{
177    apr_file_t *fp;
178    apr_status_t rv;
179    apr_size_t nbytes;
180    const char *storename;
181    unsigned char digest[APR_MD5_DIGESTSIZE];
182
183    storename = slotmem_filename(slotmem->gpool, slotmem->name, 1);
184
185    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02334)
186                 "storing %s", storename);
187
188    if (storename) {
189        rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE,
190                           APR_OS_DEFAULT, slotmem->gpool);
191        if (APR_STATUS_IS_EEXIST(rv)) {
192            apr_file_remove(storename, slotmem->gpool);
193            rv = apr_file_open(&fp, storename, APR_CREATE | APR_READ | APR_WRITE,
194                               APR_OS_DEFAULT, slotmem->gpool);
195        }
196        if (rv != APR_SUCCESS) {
197            return;
198        }
199        if (AP_SLOTMEM_IS_CLEARINUSE(slotmem)) {
200            slotmem_clearinuse(slotmem);
201        }
202        nbytes = (slotmem->desc.size * slotmem->desc.num) +
203                 (slotmem->desc.num * sizeof(char)) + AP_UNSIGNEDINT_OFFSET;
204        apr_md5(digest, slotmem->persist, nbytes);
205        rv = apr_file_write_full(fp, slotmem->persist, nbytes, NULL);
206        if (rv == APR_SUCCESS) {
207            rv = apr_file_write_full(fp, digest, APR_MD5_DIGESTSIZE, NULL);
208        }
209        apr_file_close(fp);
210        if (rv != APR_SUCCESS) {
211            apr_file_remove(storename, slotmem->gpool);
212        }
213    }
214}
215
216static apr_status_t restore_slotmem(void *ptr, const char *name, apr_size_t size,
217                                    apr_pool_t *pool)
218{
219    const char *storename;
220    apr_file_t *fp;
221    apr_size_t nbytes = size;
222    apr_status_t rv = APR_SUCCESS;
223    unsigned char digest[APR_MD5_DIGESTSIZE];
224    unsigned char digest2[APR_MD5_DIGESTSIZE];
225
226    storename = slotmem_filename(pool, name, 1);
227
228    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02335)
229                 "restoring %s", storename);
230
231    if (storename) {
232        rv = apr_file_open(&fp, storename, APR_READ | APR_WRITE, APR_OS_DEFAULT,
233                           pool);
234        if (rv == APR_SUCCESS) {
235            rv = apr_file_read(fp, ptr, &nbytes);
236            if ((rv == APR_SUCCESS || rv == APR_EOF) && nbytes == size) {
237                rv = APR_SUCCESS;   /* for successful return @ EOF */
238                /*
239                 * if at EOF, don't bother checking md5
240                 *  - backwards compatibility
241                 *  */
242                if (apr_file_eof(fp) != APR_EOF) {
243                    apr_size_t ds = APR_MD5_DIGESTSIZE;
244                    rv = apr_file_read(fp, digest, &ds);
245                    if ((rv == APR_SUCCESS || rv == APR_EOF) &&
246                        ds == APR_MD5_DIGESTSIZE) {
247                        rv = APR_SUCCESS;
248                        apr_md5(digest2, ptr, nbytes);
249                        if (memcmp(digest, digest2, APR_MD5_DIGESTSIZE)) {
250                            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
251                                         APLOGNO(02551) "bad md5 match");
252                            rv = APR_EGENERAL;
253                        }
254                    }
255                }
256                else {
257                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
258                                 APLOGNO(02552) "at EOF... bypassing md5 match check (old persist file?)");
259                }
260            }
261            else if (nbytes != size) {
262                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
263                             APLOGNO(02553) "Expected %" APR_SIZE_T_FMT ": Read %" APR_SIZE_T_FMT,
264                             size, nbytes);
265                rv = APR_EGENERAL;
266            }
267            apr_file_close(fp);
268        }
269    }
270    return rv;
271}
272
273static apr_status_t cleanup_slotmem(void *param)
274{
275    ap_slotmem_instance_t **mem = param;
276
277    if (*mem) {
278        ap_slotmem_instance_t *next = *mem;
279        while (next) {
280            if (AP_SLOTMEM_IS_PERSIST(next)) {
281                store_slotmem(next);
282            }
283            if (next->fbased) {
284                const char *name;
285                apr_shm_remove(next->name, next->gpool);
286                name = slotmem_filename(next->gpool, next->name, 0);
287                if (name) {
288                    apr_file_remove(name, next->gpool);
289                }
290            }
291            apr_shm_destroy((apr_shm_t *)next->shm);
292            next = next->next;
293        }
294    }
295    /* apr_pool_destroy(gpool); */
296    globallistmem = NULL;
297    return APR_SUCCESS;
298}
299
300static apr_status_t slotmem_doall(ap_slotmem_instance_t *mem,
301                                  ap_slotmem_callback_fn_t *func,
302                                  void *data, apr_pool_t *pool)
303{
304    unsigned int i;
305    char *ptr;
306    char *inuse;
307    apr_status_t retval = APR_SUCCESS;
308
309    if (!mem) {
310        return APR_ENOSHMAVAIL;
311    }
312
313    ptr = (char *)mem->base;
314    inuse = mem->inuse;
315    for (i = 0; i < mem->desc.num; i++, inuse++) {
316        if (!AP_SLOTMEM_IS_PREGRAB(mem) ||
317           (AP_SLOTMEM_IS_PREGRAB(mem) && *inuse)) {
318            retval = func((void *) ptr, data, pool);
319            if (retval != APR_SUCCESS)
320                break;
321        }
322        ptr += mem->desc.size;
323    }
324    return retval;
325}
326
327static apr_status_t slotmem_create(ap_slotmem_instance_t **new,
328                                   const char *name, apr_size_t item_size,
329                                   unsigned int item_num,
330                                   ap_slotmem_type_t type, apr_pool_t *pool)
331{
332/*    void *slotmem = NULL; */
333    int fbased = 1;
334    int restored = 0;
335    char *ptr;
336    sharedslotdesc_t desc;
337    ap_slotmem_instance_t *res;
338    ap_slotmem_instance_t *next = globallistmem;
339    const char *fname;
340    apr_shm_t *shm;
341    apr_size_t basesize = (item_size * item_num);
342    apr_size_t size = AP_SLOTMEM_OFFSET + AP_UNSIGNEDINT_OFFSET +
343                      (item_num * sizeof(char)) + basesize;
344    apr_status_t rv;
345
346    if (gpool == NULL) {
347        return APR_ENOSHMAVAIL;
348    }
349    fname = slotmem_filename(pool, name, 0);
350    if (fname) {
351        /* first try to attach to existing slotmem */
352        if (next) {
353            for (;;) {
354                if (strcmp(next->name, fname) == 0) {
355                    /* we already have it */
356                    *new = next;
357                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02603)
358                                 "create found %s in global list", fname);
359                    return APR_SUCCESS;
360                }
361                if (!next->next) {
362                     break;
363                }
364                next = next->next;
365            }
366        }
367        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02602)
368                     "create didn't find %s in global list", fname);
369    }
370    else {
371        fbased = 0;
372        fname = "none";
373    }
374
375    /* first try to attach to existing shared memory */
376    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02300)
377                 "create %s: %"APR_SIZE_T_FMT"/%u", fname, item_size,
378                 item_num);
379    if (fbased) {
380        rv = apr_shm_attach(&shm, fname, gpool);
381    }
382    else {
383        rv = APR_EINVAL;
384    }
385    if (rv == APR_SUCCESS) {
386        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02598)
387                     "apr_shm_attach() succeeded");
388
389        /* check size */
390        if (apr_shm_size_get(shm) != size) {
391            apr_shm_detach(shm);
392            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(02599)
393                         "existing shared memory for %s could not be used (failed size check)",
394                         fname);
395            return APR_EINVAL;
396        }
397        ptr = (char *)apr_shm_baseaddr_get(shm);
398        memcpy(&desc, ptr, sizeof(desc));
399        if (desc.size != item_size || desc.num != item_num) {
400            apr_shm_detach(shm);
401            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(02600)
402                         "existing shared memory for %s could not be used (failed contents check)",
403                         fname);
404            return APR_EINVAL;
405        }
406        ptr += AP_SLOTMEM_OFFSET;
407    }
408    else {
409        apr_size_t dsize = size - AP_SLOTMEM_OFFSET;
410        if (fbased) {
411            apr_shm_remove(fname, gpool);
412            rv = apr_shm_create(&shm, size, fname, gpool);
413        }
414        else {
415            rv = apr_shm_create(&shm, size, NULL, gpool);
416        }
417        ap_log_error(APLOG_MARK, rv == APR_SUCCESS ? APLOG_DEBUG : APLOG_ERR,
418                     rv, ap_server_conf, APLOGNO(02611)
419                     "create: apr_shm_create(%s) %s",
420                     fname ? fname : "",
421                     rv == APR_SUCCESS ? "succeeded" : "failed");
422        if (rv != APR_SUCCESS) {
423            return rv;
424        }
425        if (fbased) {
426            /* Set permissions to shared memory
427             * so it can be attached by child process
428             * having different user credentials
429             *
430             * See apr:shmem/unix/shm.c
431             */
432            unixd_set_shm_perms(fname);
433        }
434        ptr = (char *)apr_shm_baseaddr_get(shm);
435        desc.size = item_size;
436        desc.num = item_num;
437        desc.type = type;
438        memcpy(ptr, &desc, sizeof(desc));
439        ptr += AP_SLOTMEM_OFFSET;
440        memset(ptr, 0, dsize);
441        /*
442         * TODO: Error check the below... What error makes
443         * sense if the restore fails? Any?
444         */
445        if (type & AP_SLOTMEM_TYPE_PERSIST) {
446            rv = restore_slotmem(ptr, fname, dsize, pool);
447            if (rv == APR_SUCCESS) {
448                restored = 1;
449            }
450            else {
451                /* just in case, re-zero */
452                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
453                             APLOGNO(02554) "could not restore %s", fname);
454                memset(ptr, 0, dsize);
455            }
456        }
457    }
458
459    /* For the chained slotmem stuff */
460    res = (ap_slotmem_instance_t *) apr_pcalloc(gpool,
461                                                sizeof(ap_slotmem_instance_t));
462    res->name = apr_pstrdup(gpool, fname);
463    res->fbased = fbased;
464    res->shm = shm;
465    res->num_free = (unsigned int *)ptr;
466    if (!restored) {
467        *res->num_free = item_num;
468    }
469    res->persist = (void *)ptr;
470    ptr += AP_UNSIGNEDINT_OFFSET;
471    res->base = (void *)ptr;
472    res->desc = desc;
473    res->gpool = gpool;
474    res->next = NULL;
475    res->inuse = ptr + basesize;
476    if (globallistmem == NULL) {
477        globallistmem = res;
478    }
479    else {
480        next->next = res;
481    }
482
483    *new = res;
484    return APR_SUCCESS;
485}
486
487static apr_status_t slotmem_attach(ap_slotmem_instance_t **new,
488                                   const char *name, apr_size_t *item_size,
489                                   unsigned int *item_num, apr_pool_t *pool)
490{
491/*    void *slotmem = NULL; */
492    char *ptr;
493    ap_slotmem_instance_t *res;
494    ap_slotmem_instance_t *next = globallistmem;
495    sharedslotdesc_t desc;
496    const char *fname;
497    apr_shm_t *shm;
498    apr_status_t rv;
499
500    if (gpool == NULL) {
501        return APR_ENOSHMAVAIL;
502    }
503    fname = slotmem_filename(pool, name, 0);
504    if (!fname) {
505        return APR_ENOSHMAVAIL;
506    }
507
508    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02301)
509                 "attach looking for %s", fname);
510
511    /* first try to attach to existing slotmem */
512    if (next) {
513        for (;;) {
514            if (strcmp(next->name, fname) == 0) {
515                /* we already have it */
516                *new = next;
517                *item_size = next->desc.size;
518                *item_num = next->desc.num;
519                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
520                             APLOGNO(02302)
521                             "attach found %s: %"APR_SIZE_T_FMT"/%u", fname,
522                             *item_size, *item_num);
523                return APR_SUCCESS;
524            }
525            if (!next->next) {
526                 break;
527            }
528            next = next->next;
529        }
530    }
531
532    /* next try to attach to existing shared memory */
533    rv = apr_shm_attach(&shm, fname, gpool);
534    if (rv != APR_SUCCESS) {
535        return rv;
536    }
537
538    /* Read the description of the slotmem */
539    ptr = (char *)apr_shm_baseaddr_get(shm);
540    memcpy(&desc, ptr, sizeof(desc));
541    ptr += AP_SLOTMEM_OFFSET;
542
543    /* For the chained slotmem stuff */
544    res = (ap_slotmem_instance_t *) apr_pcalloc(gpool,
545                                                sizeof(ap_slotmem_instance_t));
546    res->name = apr_pstrdup(gpool, fname);
547    res->fbased = 1;
548    res->shm = shm;
549    res->num_free = (unsigned int *)ptr;
550    res->persist = (void *)ptr;
551    ptr += AP_UNSIGNEDINT_OFFSET;
552    res->base = (void *)ptr;
553    res->desc = desc;
554    res->gpool = gpool;
555    res->inuse = ptr + (desc.size * desc.num);
556    res->next = NULL;
557    if (globallistmem == NULL) {
558        globallistmem = res;
559    }
560    else {
561        next->next = res;
562    }
563
564    *new = res;
565    *item_size = desc.size;
566    *item_num = desc.num;
567    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
568                 APLOGNO(02303)
569                 "attach found %s: %"APR_SIZE_T_FMT"/%u", fname,
570                 *item_size, *item_num);
571    return APR_SUCCESS;
572}
573
574static apr_status_t slotmem_dptr(ap_slotmem_instance_t *slot,
575                                 unsigned int id, void **mem)
576{
577    char *ptr;
578
579    if (!slot) {
580        return APR_ENOSHMAVAIL;
581    }
582    if (id >= slot->desc.num) {
583        return APR_EINVAL;
584    }
585
586    ptr = (char *)slot->base + slot->desc.size * id;
587    if (!ptr) {
588        return APR_ENOSHMAVAIL;
589    }
590    *mem = (void *)ptr;
591    return APR_SUCCESS;
592}
593
594static apr_status_t slotmem_get(ap_slotmem_instance_t *slot, unsigned int id,
595                                unsigned char *dest, apr_size_t dest_len)
596{
597    void *ptr;
598    char *inuse;
599    apr_status_t ret;
600
601    if (!slot) {
602        return APR_ENOSHMAVAIL;
603    }
604
605    inuse = slot->inuse + id;
606    if (id >= slot->desc.num) {
607        return APR_EINVAL;
608    }
609    if (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse) {
610        return APR_NOTFOUND;
611    }
612    ret = slotmem_dptr(slot, id, &ptr);
613    if (ret != APR_SUCCESS) {
614        return ret;
615    }
616    *inuse = 1;
617    memcpy(dest, ptr, dest_len); /* bounds check? */
618    return APR_SUCCESS;
619}
620
621static apr_status_t slotmem_put(ap_slotmem_instance_t *slot, unsigned int id,
622                                unsigned char *src, apr_size_t src_len)
623{
624    void *ptr;
625    char *inuse;
626    apr_status_t ret;
627
628    if (!slot) {
629        return APR_ENOSHMAVAIL;
630    }
631
632    inuse = slot->inuse + id;
633    if (id >= slot->desc.num) {
634        return APR_EINVAL;
635    }
636    if (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse) {
637        return APR_NOTFOUND;
638    }
639    ret = slotmem_dptr(slot, id, &ptr);
640    if (ret != APR_SUCCESS) {
641        return ret;
642    }
643    *inuse=1;
644    memcpy(ptr, src, src_len); /* bounds check? */
645    return APR_SUCCESS;
646}
647
648static unsigned int slotmem_num_slots(ap_slotmem_instance_t *slot)
649{
650    return slot->desc.num;
651}
652
653static unsigned int slotmem_num_free_slots(ap_slotmem_instance_t *slot)
654{
655    if (AP_SLOTMEM_IS_PREGRAB(slot))
656        return *slot->num_free;
657    else {
658        unsigned int i, counter=0;
659        char *inuse = slot->inuse;
660        for (i=0; i<slot->desc.num; i++, inuse++) {
661            if (!*inuse)
662                counter++;
663        }
664        return counter;
665    }
666}
667
668static apr_size_t slotmem_slot_size(ap_slotmem_instance_t *slot)
669{
670    return slot->desc.size;
671}
672
673static apr_status_t slotmem_grab(ap_slotmem_instance_t *slot, unsigned int *id)
674{
675    unsigned int i;
676    char *inuse;
677
678    if (!slot) {
679        return APR_ENOSHMAVAIL;
680    }
681
682    inuse = slot->inuse;
683
684    for (i = 0; i < slot->desc.num; i++, inuse++) {
685        if (!*inuse) {
686            break;
687        }
688    }
689    if (i >= slot->desc.num) {
690        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02293)
691                     "slotmem(%s) grab failed. Num %u/num_free %u",
692                     slot->name, slotmem_num_slots(slot),
693                     slotmem_num_free_slots(slot));
694        return APR_EINVAL;
695    }
696    *inuse = 1;
697    *id = i;
698    (*slot->num_free)--;
699    return APR_SUCCESS;
700}
701
702static apr_status_t slotmem_fgrab(ap_slotmem_instance_t *slot, unsigned int id)
703{
704    char *inuse;
705
706    if (!slot) {
707        return APR_ENOSHMAVAIL;
708    }
709
710    if (id >= slot->desc.num) {
711        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02397)
712                     "slotmem(%s) fgrab failed. Num %u/num_free %u",
713                     slot->name, slotmem_num_slots(slot),
714                     slotmem_num_free_slots(slot));
715        return APR_EINVAL;
716    }
717    inuse = slot->inuse + id;
718
719    if (!*inuse) {
720        *inuse = 1;
721        (*slot->num_free)--;
722    }
723    return APR_SUCCESS;
724}
725
726static apr_status_t slotmem_release(ap_slotmem_instance_t *slot,
727                                    unsigned int id)
728{
729    char *inuse;
730
731    if (!slot) {
732        return APR_ENOSHMAVAIL;
733    }
734
735    inuse = slot->inuse;
736
737    if (id >= slot->desc.num || !inuse[id] ) {
738        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02294)
739                     "slotmem(%s) release failed. Num %u/inuse[%u] %d",
740                     slot->name, slotmem_num_slots(slot),
741                     id, (int)inuse[id]);
742        if (id >= slot->desc.num) {
743            return APR_EINVAL;
744        } else {
745            return APR_NOTFOUND;
746        }
747    }
748    inuse[id] = 0;
749    (*slot->num_free)++;
750    return APR_SUCCESS;
751}
752
753static const ap_slotmem_provider_t storage = {
754    "sharedmem",
755    &slotmem_doall,
756    &slotmem_create,
757    &slotmem_attach,
758    &slotmem_dptr,
759    &slotmem_get,
760    &slotmem_put,
761    &slotmem_num_slots,
762    &slotmem_num_free_slots,
763    &slotmem_slot_size,
764    &slotmem_grab,
765    &slotmem_release,
766    &slotmem_fgrab
767};
768
769/* make the storage usuable from outside */
770static const ap_slotmem_provider_t *slotmem_shm_getstorage(void)
771{
772    return (&storage);
773}
774
775/* initialise the global pool */
776static void slotmem_shm_initgpool(apr_pool_t *p)
777{
778    gpool = p;
779}
780
781/* Add the pool_clean routine */
782static void slotmem_shm_initialize_cleanup(apr_pool_t *p)
783{
784    apr_pool_cleanup_register(p, &globallistmem, cleanup_slotmem,
785                              apr_pool_cleanup_null);
786}
787
788/*
789 * Make sure the shared memory is cleaned
790 */
791static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
792                       server_rec *s)
793{
794    slotmem_shm_initialize_cleanup(p);
795    return OK;
796}
797
798static int pre_config(apr_pool_t *p, apr_pool_t *plog,
799                      apr_pool_t *ptemp)
800{
801    slotmem_shm_initgpool(p);
802    return OK;
803}
804
805static void ap_slotmem_shm_register_hook(apr_pool_t *p)
806{
807    const ap_slotmem_provider_t *storage = slotmem_shm_getstorage();
808    ap_register_provider(p, AP_SLOTMEM_PROVIDER_GROUP, "shm",
809                         AP_SLOTMEM_PROVIDER_VERSION, storage);
810    ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_LAST);
811    ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE);
812}
813
814AP_DECLARE_MODULE(slotmem_shm) = {
815    STANDARD20_MODULE_STUFF,
816    NULL,                       /* create per-directory config structure */
817    NULL,                       /* merge per-directory config structures */
818    NULL,                       /* create per-server config structure */
819    NULL,                       /* merge per-server config structures */
820    NULL,                       /* command apr_table_t */
821    ap_slotmem_shm_register_hook  /* register hooks */
822};
823