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 plain memory divided in slot.
18 * This one uses plain memory.
19 */
20
21#include  "ap_slotmem.h"
22
23#define AP_SLOTMEM_IS_PREGRAB(t) (t->type & AP_SLOTMEM_TYPE_PREGRAB)
24
25struct ap_slotmem_instance_t {
26    char                 *name;       /* per segment name */
27    void                 *base;       /* data set start */
28    apr_size_t           size;        /* size of each memory slot */
29    unsigned int         num;         /* number of mem slots */
30    apr_pool_t           *gpool;      /* per segment global pool */
31    char                 *inuse;      /* in-use flag table*/
32    ap_slotmem_type_t    type;        /* type-specific flags */
33    struct ap_slotmem_instance_t  *next;       /* location of next allocated segment */
34};
35
36
37/* global pool and list of slotmem we are handling */
38static struct ap_slotmem_instance_t *globallistmem = NULL;
39static apr_pool_t *gpool = NULL;
40
41static apr_status_t slotmem_do(ap_slotmem_instance_t *mem, ap_slotmem_callback_fn_t *func, void *data, apr_pool_t *pool)
42{
43    unsigned int i;
44    char *ptr;
45    char *inuse;
46    apr_status_t retval = APR_SUCCESS;
47
48
49    if (!mem)
50        return APR_ENOSHMAVAIL;
51
52    ptr = (char *)mem->base;
53    inuse = mem->inuse;
54    for (i = 0; i < mem->num; i++, inuse++) {
55        if (!AP_SLOTMEM_IS_PREGRAB(mem) ||
56           (AP_SLOTMEM_IS_PREGRAB(mem) && *inuse)) {
57            retval = func((void *) ptr, data, pool);
58            if (retval != APR_SUCCESS)
59                break;
60        }
61        ptr += mem->size;
62    }
63    return retval;
64}
65
66static 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)
67{
68    ap_slotmem_instance_t *res;
69    ap_slotmem_instance_t *next = globallistmem;
70    apr_size_t basesize = (item_size * item_num);
71
72    const char *fname;
73
74    if (name) {
75        if (name[0] == ':')
76            fname = name;
77        else
78            fname = ap_runtime_dir_relative(pool, name);
79
80        /* first try to attach to existing slotmem */
81        if (next) {
82            for (;;) {
83                if (strcmp(next->name, fname) == 0) {
84                    /* we already have it */
85                    *new = next;
86                    return APR_SUCCESS;
87                }
88                if (!next->next) {
89                     break;
90                }
91                next = next->next;
92            }
93        }
94    }
95    else
96        fname = "anonymous";
97
98    /* create the memory using the gpool */
99    res = (ap_slotmem_instance_t *) apr_pcalloc(gpool, sizeof(ap_slotmem_instance_t));
100    res->base = apr_pcalloc(gpool, basesize + (item_num * sizeof(char)));
101    if (!res->base)
102        return APR_ENOSHMAVAIL;
103
104    /* For the chained slotmem stuff */
105    res->name = apr_pstrdup(gpool, fname);
106    res->size = item_size;
107    res->num = item_num;
108    res->next = NULL;
109    res->type = type;
110    res->inuse = (char *)res->base + basesize;
111    if (globallistmem == NULL)
112        globallistmem = res;
113    else
114        next->next = res;
115
116    *new = res;
117    return APR_SUCCESS;
118}
119
120static 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)
121{
122    ap_slotmem_instance_t *next = globallistmem;
123    const char *fname;
124
125    if (name) {
126        if (name[0] == ':')
127            fname = name;
128        else
129            fname = ap_runtime_dir_relative(pool, name);
130    }
131    else
132        return APR_ENOSHMAVAIL;
133
134    /* first try to attach to existing slotmem */
135    while (next) {
136        if (strcmp(next->name, fname) == 0) {
137            /* we already have it */
138            *new = next;
139            *item_size = next->size;
140            *item_num = next->num;
141            return APR_SUCCESS;
142        }
143        next = next->next;
144    }
145
146    return APR_ENOSHMAVAIL;
147}
148
149static apr_status_t slotmem_dptr(ap_slotmem_instance_t *score, unsigned int id, void **mem)
150{
151
152    char *ptr;
153
154    if (!score)
155        return APR_ENOSHMAVAIL;
156    if (id >= score->num)
157        return APR_EINVAL;
158
159    ptr = (char *)score->base + score->size * id;
160    if (!ptr)
161        return APR_ENOSHMAVAIL;
162    *mem = ptr;
163    return APR_SUCCESS;
164}
165
166static apr_status_t slotmem_get(ap_slotmem_instance_t *slot, unsigned int id, unsigned char *dest, apr_size_t dest_len)
167{
168    void *ptr;
169    char *inuse;
170    apr_status_t ret;
171
172    if (!slot) {
173        return APR_ENOSHMAVAIL;
174    }
175
176    inuse = slot->inuse + id;
177    if (id >= slot->num) {
178        return APR_EINVAL;
179    }
180    if (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse) {
181        return APR_NOTFOUND;
182    }
183    ret = slotmem_dptr(slot, id, &ptr);
184    if (ret != APR_SUCCESS) {
185        return ret;
186    }
187    *inuse=1;
188    memcpy(dest, ptr, dest_len); /* bounds check? */
189    return APR_SUCCESS;
190}
191
192static apr_status_t slotmem_put(ap_slotmem_instance_t *slot, unsigned int id, unsigned char *src, apr_size_t src_len)
193{
194    void *ptr;
195    char *inuse;
196    apr_status_t ret;
197
198    if (!slot) {
199        return APR_ENOSHMAVAIL;
200    }
201
202    inuse = slot->inuse + id;
203    if (id >= slot->num) {
204        return APR_EINVAL;
205    }
206    if (AP_SLOTMEM_IS_PREGRAB(slot) && !*inuse) {
207        return APR_NOTFOUND;
208    }
209    ret = slotmem_dptr(slot, id, &ptr);
210    if (ret != APR_SUCCESS) {
211        return ret;
212    }
213    *inuse=1;
214    memcpy(ptr, src, src_len); /* bounds check? */
215    return APR_SUCCESS;
216}
217
218static unsigned int slotmem_num_slots(ap_slotmem_instance_t *slot)
219{
220    return slot->num;
221}
222
223static unsigned int slotmem_num_free_slots(ap_slotmem_instance_t *slot)
224{
225    unsigned int i, counter=0;
226    char *inuse = slot->inuse;
227    for (i = 0; i < slot->num; i++, inuse++) {
228        if (!*inuse)
229            counter++;
230    }
231    return counter;
232}
233
234static apr_size_t slotmem_slot_size(ap_slotmem_instance_t *slot)
235{
236    return slot->size;
237}
238
239/*
240 * XXXX: if !AP_SLOTMEM_IS_PREGRAB, then still worry about
241 *       inuse for grab and return?
242 */
243static apr_status_t slotmem_grab(ap_slotmem_instance_t *slot, unsigned int *id)
244{
245    unsigned int i;
246    char *inuse;
247
248    if (!slot) {
249        return APR_ENOSHMAVAIL;
250    }
251
252    inuse = slot->inuse;
253
254    for (i = 0; i < slot->num; i++, inuse++) {
255        if (!*inuse) {
256            break;
257        }
258    }
259    if (i >= slot->num) {
260        return APR_EINVAL;
261    }
262    *inuse = 1;
263    *id = i;
264    return APR_SUCCESS;
265}
266
267static apr_status_t slotmem_fgrab(ap_slotmem_instance_t *slot, unsigned int id)
268{
269    char *inuse;
270
271    if (!slot) {
272        return APR_ENOSHMAVAIL;
273    }
274
275    if (id >= slot->num) {
276        return APR_EINVAL;
277    }
278    inuse = slot->inuse + id;
279    *inuse = 1;
280    return APR_SUCCESS;
281}
282
283static apr_status_t slotmem_release(ap_slotmem_instance_t *slot, unsigned int id)
284{
285    char *inuse;
286
287    if (!slot) {
288        return APR_ENOSHMAVAIL;
289    }
290
291    inuse = slot->inuse;
292
293    if (id >= slot->num) {
294        return APR_EINVAL;
295    }
296    if (!inuse[id] ) {
297        return APR_NOTFOUND;
298    }
299    inuse[id] = 0;
300    return APR_SUCCESS;
301}
302
303static const ap_slotmem_provider_t storage = {
304    "plainmem",
305    &slotmem_do,
306    &slotmem_create,
307    &slotmem_attach,
308    &slotmem_dptr,
309    &slotmem_get,
310    &slotmem_put,
311    &slotmem_num_slots,
312    &slotmem_num_free_slots,
313    &slotmem_slot_size,
314    &slotmem_grab,
315    &slotmem_release,
316    &slotmem_fgrab
317};
318
319static int pre_config(apr_pool_t *p, apr_pool_t *plog,
320                      apr_pool_t *ptemp)
321{
322    gpool = p;
323    return OK;
324}
325
326static void ap_slotmem_plain_register_hook(apr_pool_t *p)
327{
328    /* XXX: static const char * const prePos[] = { "mod_slotmem.c", NULL }; */
329    ap_register_provider(p, AP_SLOTMEM_PROVIDER_GROUP, "plain",
330                         AP_SLOTMEM_PROVIDER_VERSION, &storage);
331    ap_hook_pre_config(pre_config, NULL, NULL, APR_HOOK_MIDDLE);
332}
333
334AP_DECLARE_MODULE(slotmem_plain) = {
335    STANDARD20_MODULE_STUFF,
336    NULL,                        /* create per-directory config structure */
337    NULL,                        /* merge per-directory config structures */
338    NULL,                        /* create per-server config structure */
339    NULL,                        /* merge per-server config structures */
340    NULL,                        /* command apr_table_t */
341    ap_slotmem_plain_register_hook    /* register hooks */
342};
343
344