Blame modules/cache/mod_socache_shmcb.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#include "httpd.h"
Packit 90a5c9
#include "http_log.h"
Packit 90a5c9
#include "http_request.h"
Packit 90a5c9
#include "http_protocol.h"
Packit 90a5c9
#include "http_config.h"
Packit 90a5c9
#include "mod_status.h"
Packit 90a5c9
Packit 90a5c9
#include "apr.h"
Packit 90a5c9
#include "apr_strings.h"
Packit 90a5c9
#include "apr_time.h"
Packit 90a5c9
#include "apr_shm.h"
Packit 90a5c9
#define APR_WANT_STRFUNC
Packit 90a5c9
#include "apr_want.h"
Packit 90a5c9
#include "apr_general.h"
Packit 90a5c9
Packit 90a5c9
#if APR_HAVE_LIMITS_H
Packit 90a5c9
#include <limits.h>
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
#include "ap_socache.h"
Packit 90a5c9
Packit 90a5c9
/* XXX Unfortunately, there are still many unsigned ints in use here, so we
Packit 90a5c9
 * XXX cannot allow more than UINT_MAX. Since some of the ints are exposed in
Packit 90a5c9
 * XXX public interfaces, a simple search and replace is not enough.
Packit 90a5c9
 * XXX It should be possible to extend that so that the total cache size can
Packit 90a5c9
 * XXX be APR_SIZE_MAX and only the object size needs to be smaller than
Packit 90a5c9
 * XXX UINT_MAX.
Packit 90a5c9
 */
Packit 90a5c9
#define SHMCB_MAX_SIZE (UINT_MAX
Packit 90a5c9
Packit 90a5c9
#define DEFAULT_SHMCB_PREFIX "socache-shmcb-"
Packit 90a5c9
Packit 90a5c9
#define DEFAULT_SHMCB_SUFFIX ".cache"
Packit 90a5c9
Packit 90a5c9
#define ALIGNED_HEADER_SIZE APR_ALIGN_DEFAULT(sizeof(SHMCBHeader))
Packit 90a5c9
#define ALIGNED_SUBCACHE_SIZE APR_ALIGN_DEFAULT(sizeof(SHMCBSubcache))
Packit 90a5c9
#define ALIGNED_INDEX_SIZE APR_ALIGN_DEFAULT(sizeof(SHMCBIndex))
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Header structure - the start of the shared-mem segment
Packit 90a5c9
 */
Packit 90a5c9
typedef struct {
Packit 90a5c9
    /* Stats for cache operations */
Packit 90a5c9
    unsigned long stat_stores;
Packit 90a5c9
    unsigned long stat_replaced;
Packit 90a5c9
    unsigned long stat_expiries;
Packit 90a5c9
    unsigned long stat_scrolled;
Packit 90a5c9
    unsigned long stat_retrieves_hit;
Packit 90a5c9
    unsigned long stat_retrieves_miss;
Packit 90a5c9
    unsigned long stat_removes_hit;
Packit 90a5c9
    unsigned long stat_removes_miss;
Packit 90a5c9
    /* Number of subcaches */
Packit 90a5c9
    unsigned int subcache_num;
Packit 90a5c9
    /* How many indexes each subcache's queue has */
Packit 90a5c9
    unsigned int index_num;
Packit 90a5c9
    /* How large each subcache is, including the queue and data */
Packit 90a5c9
    unsigned int subcache_size;
Packit 90a5c9
    /* How far into each subcache the data area is (optimisation) */
Packit 90a5c9
    unsigned int subcache_data_offset;
Packit 90a5c9
    /* How large the data area in each subcache is (optimisation) */
Packit 90a5c9
    unsigned int subcache_data_size;
Packit 90a5c9
} SHMCBHeader;
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Subcache structure - the start of each subcache, followed by
Packit 90a5c9
 * indexes then data
Packit 90a5c9
 */
Packit 90a5c9
typedef struct {
Packit 90a5c9
    /* The start position and length of the cyclic buffer of indexes */
Packit 90a5c9
    unsigned int idx_pos, idx_used;
Packit 90a5c9
    /* Same for the data area */
Packit 90a5c9
    unsigned int data_pos, data_used;
Packit 90a5c9
} SHMCBSubcache;
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Index structure - each subcache has an array of these
Packit 90a5c9
 */
Packit 90a5c9
typedef struct {
Packit 90a5c9
    /* absolute time this entry expires */
Packit 90a5c9
    apr_time_t expires;
Packit 90a5c9
    /* location within the subcache's data area */
Packit 90a5c9
    unsigned int data_pos;
Packit 90a5c9
    /* size (most logic ignores this, we keep it only to minimise memcpy) */
Packit 90a5c9
    unsigned int data_used;
Packit 90a5c9
    /* length of the used data which contains the id */
Packit 90a5c9
    unsigned int id_len;
Packit 90a5c9
    /* Used to mark explicitly-removed socache entries */
Packit 90a5c9
    unsigned char removed;
Packit 90a5c9
} SHMCBIndex;
Packit 90a5c9
Packit 90a5c9
struct ap_socache_instance_t {
Packit 90a5c9
    const char *data_file;
Packit 90a5c9
    apr_size_t shm_size;
Packit 90a5c9
    apr_shm_t *shm;
Packit 90a5c9
    SHMCBHeader *header;
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
/* The SHM data segment is of fixed size and stores data as follows.
Packit 90a5c9
 *
Packit 90a5c9
 *   [ SHMCBHeader | Subcaches ]
Packit 90a5c9
 *
Packit 90a5c9
 * The SHMCBHeader header structure stores metadata concerning the
Packit 90a5c9
 * cache and the contained subcaches.
Packit 90a5c9
 *
Packit 90a5c9
 * Subcaches is a hash table of header->subcache_num SHMCBSubcache
Packit 90a5c9
 * structures.  The hash table is indexed by SHMCB_MASK(id). Each
Packit 90a5c9
 * SHMCBSubcache structure has a fixed size (header->subcache_size),
Packit 90a5c9
 * which is determined at creation time, and looks like the following:
Packit 90a5c9
 *
Packit 90a5c9
 *   [ SHMCBSubcache | Indexes | Data ]
Packit 90a5c9
 *
Packit 90a5c9
 * Each subcache is prefixed by the SHMCBSubcache structure.
Packit 90a5c9
 *
Packit 90a5c9
 * The subcache's "Data" segment is a single cyclic data buffer, of
Packit 90a5c9
 * total size header->subcache_data_size; data inside is referenced
Packit 90a5c9
 * using byte offsets. The offset marking the beginning of the cyclic
Packit 90a5c9
 * buffer is subcache->data_pos; the buffer's length is
Packit 90a5c9
 * subcache->data_used.
Packit 90a5c9
 *
Packit 90a5c9
 * "Indexes" is an array of header->index_num SHMCBIndex structures,
Packit 90a5c9
 * which is used as a cyclic queue; subcache->idx_pos gives the array
Packit 90a5c9
 * index of the first in use, subcache->idx_used gives the number in
Packit 90a5c9
 * use.  Both ->idx_* values have a range of [0, header->index_num)
Packit 90a5c9
 *
Packit 90a5c9
 * Each in-use SHMCBIndex structure represents a single cached object.
Packit 90a5c9
 * The ID and data segment are stored consecutively in the subcache's
Packit 90a5c9
 * cyclic data buffer.  The "Data" segment can thus be seen to
Packit 90a5c9
 * look like this, for example
Packit 90a5c9
 *
Packit 90a5c9
 * offset:  [ 0     1     2     3     4     5     6    ...
Packit 90a5c9
 * contents:[ ID1   Data1       ID2   Data2       ID3  ...
Packit 90a5c9
 *
Packit 90a5c9
 * where the corresponding indices would look like:
Packit 90a5c9
 *
Packit 90a5c9
 * idx1 = { data_pos = 0, data_used = 3, id_len = 1, ...}
Packit 90a5c9
 * idx2 = { data_pos = 3, data_used = 3, id_len = 1, ...}
Packit 90a5c9
 * ...
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/* This macro takes a pointer to the header and a zero-based index and returns
Packit 90a5c9
 * a pointer to the corresponding subcache. */
Packit 90a5c9
#define SHMCB_SUBCACHE(pHeader, num) \
Packit 90a5c9
                (SHMCBSubcache *)(((unsigned char *)(pHeader)) + \
Packit 90a5c9
                        ALIGNED_HEADER_SIZE + \
Packit 90a5c9
                        (num) * ((pHeader)->subcache_size))
Packit 90a5c9
Packit 90a5c9
/* This macro takes a pointer to the header and an id and returns a
Packit 90a5c9
 * pointer to the corresponding subcache. */
Packit 90a5c9
#define SHMCB_MASK(pHeader, id) \
Packit 90a5c9
                SHMCB_SUBCACHE((pHeader), *(id) & ((pHeader)->subcache_num - 1))
Packit 90a5c9
Packit 90a5c9
/* This macro takes the same params as the last, generating two outputs for use
Packit 90a5c9
 * in ap_log_error(...). */
Packit 90a5c9
#define SHMCB_MASK_DBG(pHeader, id) \
Packit 90a5c9
                *(id), (*(id) & ((pHeader)->subcache_num - 1))
Packit 90a5c9
Packit 90a5c9
/* This macro takes a pointer to a subcache and a zero-based index and returns
Packit 90a5c9
 * a pointer to the corresponding SHMCBIndex. */
Packit 90a5c9
#define SHMCB_INDEX(pSubcache, num) \
Packit 90a5c9
                (SHMCBIndex *)(((unsigned char *)pSubcache) + \
Packit 90a5c9
                        ALIGNED_SUBCACHE_SIZE + \
Packit 90a5c9
                        (num) * ALIGNED_INDEX_SIZE)
Packit 90a5c9
Packit 90a5c9
/* This macro takes a pointer to the header and a subcache and returns a
Packit 90a5c9
 * pointer to the corresponding data area. */
Packit 90a5c9
#define SHMCB_DATA(pHeader, pSubcache) \
Packit 90a5c9
                ((unsigned char *)(pSubcache) + (pHeader)->subcache_data_offset)
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Cyclic functions - assists in "wrap-around"/modulo logic
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/* Addition modulo 'mod' */
Packit 90a5c9
#define SHMCB_CYCLIC_INCREMENT(val,inc,mod) \
Packit 90a5c9
                (((val) + (inc)) % (mod))
Packit 90a5c9
Packit 90a5c9
/* Subtraction (or "distance between") modulo 'mod' */
Packit 90a5c9
#define SHMCB_CYCLIC_SPACE(val1,val2,mod) \
Packit 90a5c9
                ((val2) >= (val1) ? ((val2) - (val1)) : \
Packit 90a5c9
                        ((val2) + (mod) - (val1)))
Packit 90a5c9
Packit 90a5c9
/* A "normal-to-cyclic" memcpy. */
Packit 90a5c9
static void shmcb_cyclic_ntoc_memcpy(unsigned int buf_size, unsigned char *data,
Packit 90a5c9
                                     unsigned int dest_offset, const unsigned char *src,
Packit 90a5c9
                                     unsigned int src_len)
Packit 90a5c9
{
Packit 90a5c9
    if (dest_offset + src_len < buf_size)
Packit 90a5c9
        /* It be copied all in one go */
Packit 90a5c9
        memcpy(data + dest_offset, src, src_len);
Packit 90a5c9
    else {
Packit 90a5c9
        /* Copy the two splits */
Packit 90a5c9
        memcpy(data + dest_offset, src, buf_size - dest_offset);
Packit 90a5c9
        memcpy(data, src + buf_size - dest_offset,
Packit 90a5c9
               src_len + dest_offset - buf_size);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* A "cyclic-to-normal" memcpy. */
Packit 90a5c9
static void shmcb_cyclic_cton_memcpy(unsigned int buf_size, unsigned char *dest,
Packit 90a5c9
                                     const unsigned char *data, unsigned int src_offset,
Packit 90a5c9
                                     unsigned int src_len)
Packit 90a5c9
{
Packit 90a5c9
    if (src_offset + src_len < buf_size)
Packit 90a5c9
        /* It be copied all in one go */
Packit 90a5c9
        memcpy(dest, data + src_offset, src_len);
Packit 90a5c9
    else {
Packit 90a5c9
        /* Copy the two splits */
Packit 90a5c9
        memcpy(dest, data + src_offset, buf_size - src_offset);
Packit 90a5c9
        memcpy(dest + buf_size - src_offset, data,
Packit 90a5c9
               src_len + src_offset - buf_size);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* A memcmp against a cyclic data buffer.  Compares SRC of length
Packit 90a5c9
 * SRC_LEN against the contents of cyclic buffer DATA (which is of
Packit 90a5c9
 * size BUF_SIZE), starting at offset DEST_OFFSET. Got that?  Good. */
Packit 90a5c9
static int shmcb_cyclic_memcmp(unsigned int buf_size, unsigned char *data,
Packit 90a5c9
                               unsigned int dest_offset,
Packit 90a5c9
                               const unsigned char *src,
Packit 90a5c9
                               unsigned int src_len)
Packit 90a5c9
{
Packit 90a5c9
    if (dest_offset + src_len < buf_size)
Packit 90a5c9
        /* It be compared all in one go */
Packit 90a5c9
        return memcmp(data + dest_offset, src, src_len);
Packit 90a5c9
    else {
Packit 90a5c9
        /* Compare the two splits */
Packit 90a5c9
        int diff;
Packit 90a5c9
Packit 90a5c9
        diff = memcmp(data + dest_offset, src, buf_size - dest_offset);
Packit 90a5c9
        if (diff) {
Packit 90a5c9
            return diff;
Packit 90a5c9
        }
Packit 90a5c9
        return memcmp(data, src + buf_size - dest_offset,
Packit 90a5c9
                      src_len + dest_offset - buf_size);
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/* Prototypes for low-level subcache operations */
Packit 90a5c9
static void shmcb_subcache_expire(server_rec *, SHMCBHeader *, SHMCBSubcache *,
Packit 90a5c9
                                  apr_time_t);
Packit 90a5c9
/* Returns zero on success, non-zero on failure. */
Packit 90a5c9
static int shmcb_subcache_store(server_rec *s, SHMCBHeader *header,
Packit 90a5c9
                                SHMCBSubcache *subcache,
Packit 90a5c9
                                unsigned char *data, unsigned int data_len,
Packit 90a5c9
                                const unsigned char *id, unsigned int id_len,
Packit 90a5c9
                                apr_time_t expiry);
Packit 90a5c9
/* Returns zero on success, non-zero on failure. */
Packit 90a5c9
static int shmcb_subcache_retrieve(server_rec *, SHMCBHeader *, SHMCBSubcache *,
Packit 90a5c9
                                   const unsigned char *id, unsigned int idlen,
Packit 90a5c9
                                   unsigned char *data, unsigned int *datalen);
Packit 90a5c9
/* Returns zero on success, non-zero on failure. */
Packit 90a5c9
static int shmcb_subcache_remove(server_rec *, SHMCBHeader *, SHMCBSubcache *,
Packit 90a5c9
                                 const unsigned char *, unsigned int);
Packit 90a5c9
Packit 90a5c9
/* Returns result of the (iterator)() call, zero is success (continue) */
Packit 90a5c9
static apr_status_t shmcb_subcache_iterate(ap_socache_instance_t *instance,
Packit 90a5c9
                                           server_rec *s,
Packit 90a5c9
                                           void *userctx,
Packit 90a5c9
                                           SHMCBHeader *header,
Packit 90a5c9
                                           SHMCBSubcache *subcache,
Packit 90a5c9
                                           ap_socache_iterator_t *iterator,
Packit 90a5c9
                                           unsigned char **buf,
Packit 90a5c9
                                           apr_size_t *buf_len,
Packit 90a5c9
                                           apr_pool_t *pool,
Packit 90a5c9
                                           apr_time_t now);
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * High-Level "handlers" as per ssl_scache.c
Packit 90a5c9
 * subcache internals are deferred to shmcb_subcache_*** functions lower down
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
static const char *socache_shmcb_create(ap_socache_instance_t **context,
Packit 90a5c9
                                        const char *arg,
Packit 90a5c9
                                        apr_pool_t *tmp, apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    ap_socache_instance_t *ctx;
Packit 90a5c9
    char *path, *cp, *cp2;
Packit 90a5c9
Packit 90a5c9
    /* Allocate the context. */
Packit 90a5c9
    *context = ctx = apr_pcalloc(p, sizeof *ctx);
Packit 90a5c9
Packit 90a5c9
    ctx->shm_size  = 1024*512; /* 512KB */
Packit 90a5c9
Packit 90a5c9
    if (!arg || *arg == '\0') {
Packit 90a5c9
        /* Use defaults. */
Packit 90a5c9
        return NULL;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ctx->data_file = path = ap_server_root_relative(p, arg);
Packit 90a5c9
Packit 90a5c9
    cp = strrchr(path, '(');
Packit 90a5c9
    cp2 = path + strlen(path) - 1;
Packit 90a5c9
    if (cp) {
Packit 90a5c9
        char *endptr;
Packit 90a5c9
        if (*cp2 != ')') {
Packit 90a5c9
            return "Invalid argument: no closing parenthesis or cache size "
Packit 90a5c9
                   "missing after pathname with parenthesis";
Packit 90a5c9
        }
Packit 90a5c9
        *cp++ = '\0';
Packit 90a5c9
        *cp2  = '\0';
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
        ctx->shm_size = strtol(cp, &endptr, 10);
Packit 90a5c9
        if (endptr != cp2) {
Packit 90a5c9
            return "Invalid argument: cache size not numerical";
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (ctx->shm_size < 8192) {
Packit 90a5c9
            return "Invalid argument: size has to be >= 8192 bytes";
Packit 90a5c9
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        if (ctx->shm_size >= SHMCB_MAX_SIZE) {
Packit 90a5c9
            return apr_psprintf(tmp, "Invalid argument: size has "
Packit 90a5c9
                    "to be < %" APR_SIZE_T_FMT " bytes on this platform",
Packit 90a5c9
                    SHMCB_MAX_SIZE);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    else if (cp2 >= path && *cp2 == ')') {
Packit 90a5c9
        return "Invalid argument: no opening parenthesis";
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t socache_shmcb_init(ap_socache_instance_t *ctx,
Packit 90a5c9
                                       const char *namespace,
Packit 90a5c9
                                       const struct ap_socache_hints *hints,
Packit 90a5c9
                                       server_rec *s, apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    void *shm_segment;
Packit 90a5c9
    apr_size_t shm_segsize;
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    SHMCBHeader *header;
Packit 90a5c9
    unsigned int num_subcache, num_idx, loop;
Packit 90a5c9
    apr_size_t avg_obj_size, avg_id_len;
Packit 90a5c9
Packit 90a5c9
    /* Create shared memory segment */
Packit 90a5c9
    if (ctx->data_file == NULL) {
Packit 90a5c9
        const char *path = apr_pstrcat(p, DEFAULT_SHMCB_PREFIX, namespace,
Packit 90a5c9
                                       DEFAULT_SHMCB_SUFFIX, NULL);
Packit 90a5c9
Packit 90a5c9
        ctx->data_file = ap_runtime_dir_relative(p, path);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* Use anonymous shm by default, fall back on name-based. */
Packit 90a5c9
    rv = apr_shm_create(&ctx->shm, ctx->shm_size, NULL, p);
Packit 90a5c9
    if (APR_STATUS_IS_ENOTIMPL(rv)) {
Packit 90a5c9
        /* If anon shm isn't supported, fail if no named file was
Packit 90a5c9
         * configured successfully; the ap_server_root_relative call
Packit 90a5c9
         * above will return NULL for invalid paths. */
Packit 90a5c9
        if (ctx->data_file == NULL) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00818)
Packit 90a5c9
                         "Could not use default path '%s' for shmcb socache",
Packit 90a5c9
                         ctx->data_file);
Packit 90a5c9
            return APR_EINVAL;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        /* For a name-based segment, remove it first in case of a
Packit 90a5c9
         * previous unclean shutdown. */
Packit 90a5c9
        apr_shm_remove(ctx->data_file, p);
Packit 90a5c9
Packit 90a5c9
        rv = apr_shm_create(&ctx->shm, ctx->shm_size, ctx->data_file, p);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (rv != APR_SUCCESS) {
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00819)
Packit 90a5c9
                     "Could not allocate shared memory segment for shmcb "
Packit 90a5c9
                     "socache");
Packit 90a5c9
        return rv;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    shm_segment = apr_shm_baseaddr_get(ctx->shm);
Packit 90a5c9
    shm_segsize = apr_shm_size_get(ctx->shm);
Packit 90a5c9
    if (shm_segsize < (5 * ALIGNED_HEADER_SIZE)) {
Packit 90a5c9
        /* the segment is ridiculously small, bail out */
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00820)
Packit 90a5c9
                     "shared memory segment too small");
Packit 90a5c9
        return APR_ENOSPC;
Packit 90a5c9
    }
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00821)
Packit 90a5c9
                 "shmcb_init allocated %" APR_SIZE_T_FMT
Packit 90a5c9
                 " bytes of shared memory",
Packit 90a5c9
                 shm_segsize);
Packit 90a5c9
    /* Discount the header */
Packit 90a5c9
    shm_segsize -= ALIGNED_HEADER_SIZE;
Packit 90a5c9
    /* Select index size based on average object size hints, if given. */
Packit 90a5c9
    avg_obj_size = hints && hints->avg_obj_size ? hints->avg_obj_size : 150;
Packit 90a5c9
    avg_id_len = hints && hints->avg_id_len ? hints->avg_id_len : 30;
Packit 90a5c9
    num_idx = (shm_segsize) / (avg_obj_size + avg_id_len);
Packit 90a5c9
    num_subcache = 256;
Packit 90a5c9
    while ((num_idx / num_subcache) < (2 * num_subcache))
Packit 90a5c9
        num_subcache /= 2;
Packit 90a5c9
    num_idx /= num_subcache;
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00822)
Packit 90a5c9
                 "for %" APR_SIZE_T_FMT " bytes (%" APR_SIZE_T_FMT
Packit 90a5c9
                 " including header), recommending %u subcaches, "
Packit 90a5c9
                 "%u indexes each", shm_segsize,
Packit 90a5c9
                 shm_segsize + ALIGNED_HEADER_SIZE,
Packit 90a5c9
                 num_subcache, num_idx);
Packit 90a5c9
    if (num_idx < 5) {
Packit 90a5c9
        /* we're still too small, bail out */
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00823)
Packit 90a5c9
                     "shared memory segment too small");
Packit 90a5c9
        return APR_ENOSPC;
Packit 90a5c9
    }
Packit 90a5c9
    /* OK, we're sorted */
Packit 90a5c9
    ctx->header = header = shm_segment;
Packit 90a5c9
    header->stat_stores = 0;
Packit 90a5c9
    header->stat_replaced = 0;
Packit 90a5c9
    header->stat_expiries = 0;
Packit 90a5c9
    header->stat_scrolled = 0;
Packit 90a5c9
    header->stat_retrieves_hit = 0;
Packit 90a5c9
    header->stat_retrieves_miss = 0;
Packit 90a5c9
    header->stat_removes_hit = 0;
Packit 90a5c9
    header->stat_removes_miss = 0;
Packit 90a5c9
    header->subcache_num = num_subcache;
Packit 90a5c9
    /* Convert the subcache size (in bytes) to a value that is suitable for
Packit 90a5c9
     * structure alignment on the host platform, by rounding down if necessary. */
Packit 90a5c9
    header->subcache_size = (size_t)(shm_segsize / num_subcache);
Packit 90a5c9
    if (header->subcache_size != APR_ALIGN_DEFAULT(header->subcache_size)) {
Packit 90a5c9
        header->subcache_size = APR_ALIGN_DEFAULT(header->subcache_size) -
Packit 90a5c9
                                APR_ALIGN_DEFAULT(1);
Packit 90a5c9
    }
Packit 90a5c9
    header->subcache_data_offset = ALIGNED_SUBCACHE_SIZE +
Packit 90a5c9
                                   num_idx * ALIGNED_INDEX_SIZE;
Packit 90a5c9
    header->subcache_data_size = header->subcache_size -
Packit 90a5c9
                                 header->subcache_data_offset;
Packit 90a5c9
    header->index_num = num_idx;
Packit 90a5c9
Packit 90a5c9
    /* Output trace info */
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00824)
Packit 90a5c9
                 "shmcb_init_memory choices follow");
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00825)
Packit 90a5c9
                 "subcache_num = %u", header->subcache_num);
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00826)
Packit 90a5c9
                 "subcache_size = %u", header->subcache_size);
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00827)
Packit 90a5c9
                 "subcache_data_offset = %u", header->subcache_data_offset);
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00828)
Packit 90a5c9
                 "subcache_data_size = %u", header->subcache_data_size);
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00829)
Packit 90a5c9
                 "index_num = %u", header->index_num);
Packit 90a5c9
    /* The header is done, make the caches empty */
Packit 90a5c9
    for (loop = 0; loop < header->subcache_num; loop++) {
Packit 90a5c9
        SHMCBSubcache *subcache = SHMCB_SUBCACHE(header, loop);
Packit 90a5c9
        subcache->idx_pos = subcache->idx_used = 0;
Packit 90a5c9
        subcache->data_pos = subcache->data_used = 0;
Packit 90a5c9
    }
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(00830)
Packit 90a5c9
                 "Shared memory socache initialised");
Packit 90a5c9
    /* Success ... */
Packit 90a5c9
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void socache_shmcb_destroy(ap_socache_instance_t *ctx, server_rec *s)
Packit 90a5c9
{
Packit 90a5c9
    if (ctx && ctx->shm) {
Packit 90a5c9
        apr_shm_destroy(ctx->shm);
Packit 90a5c9
        ctx->shm = NULL;
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t socache_shmcb_store(ap_socache_instance_t *ctx,
Packit 90a5c9
                                        server_rec *s, const unsigned char *id,
Packit 90a5c9
                                        unsigned int idlen, apr_time_t expiry,
Packit 90a5c9
                                        unsigned char *encoded,
Packit 90a5c9
                                        unsigned int len_encoded,
Packit 90a5c9
                                        apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    SHMCBHeader *header = ctx->header;
Packit 90a5c9
    SHMCBSubcache *subcache = SHMCB_MASK(header, id);
Packit 90a5c9
    int tryreplace;
Packit 90a5c9
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00831)
Packit 90a5c9
                 "socache_shmcb_store (0x%02x -> subcache %d)",
Packit 90a5c9
                 SHMCB_MASK_DBG(header, id));
Packit 90a5c9
    /* XXX: Says who?  Why shouldn't this be acceptable, or padded if not? */
Packit 90a5c9
    if (idlen < 4) {
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00832) "unusably short id provided "
Packit 90a5c9
                "(%u bytes)", idlen);
Packit 90a5c9
        return APR_EINVAL;
Packit 90a5c9
    }
Packit 90a5c9
    tryreplace = shmcb_subcache_remove(s, header, subcache, id, idlen);
Packit 90a5c9
    if (shmcb_subcache_store(s, header, subcache, encoded,
Packit 90a5c9
                             len_encoded, id, idlen, expiry)) {
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00833)
Packit 90a5c9
                     "can't store an socache entry!");
Packit 90a5c9
        return APR_ENOSPC;
Packit 90a5c9
    }
Packit 90a5c9
    if (tryreplace == 0) {
Packit 90a5c9
        header->stat_replaced++;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        header->stat_stores++;
Packit 90a5c9
    }
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00834)
Packit 90a5c9
                 "leaving socache_shmcb_store successfully");
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t socache_shmcb_retrieve(ap_socache_instance_t *ctx,
Packit 90a5c9
                                           server_rec *s,
Packit 90a5c9
                                           const unsigned char *id, unsigned int idlen,
Packit 90a5c9
                                           unsigned char *dest, unsigned int *destlen,
Packit 90a5c9
                                           apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    SHMCBHeader *header = ctx->header;
Packit 90a5c9
    SHMCBSubcache *subcache = SHMCB_MASK(header, id);
Packit 90a5c9
    int rv;
Packit 90a5c9
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00835)
Packit 90a5c9
                 "socache_shmcb_retrieve (0x%02x -> subcache %d)",
Packit 90a5c9
                 SHMCB_MASK_DBG(header, id));
Packit 90a5c9
Packit 90a5c9
    /* Get the entry corresponding to the id, if it exists. */
Packit 90a5c9
    rv = shmcb_subcache_retrieve(s, header, subcache, id, idlen,
Packit 90a5c9
                                 dest, destlen);
Packit 90a5c9
    if (rv == 0)
Packit 90a5c9
        header->stat_retrieves_hit++;
Packit 90a5c9
    else
Packit 90a5c9
        header->stat_retrieves_miss++;
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00836)
Packit 90a5c9
                 "leaving socache_shmcb_retrieve successfully");
Packit 90a5c9
Packit 90a5c9
    return rv == 0 ? APR_SUCCESS : APR_NOTFOUND;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t socache_shmcb_remove(ap_socache_instance_t *ctx,
Packit 90a5c9
                                         server_rec *s, const unsigned char *id,
Packit 90a5c9
                                         unsigned int idlen, apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    SHMCBHeader *header = ctx->header;
Packit 90a5c9
    SHMCBSubcache *subcache = SHMCB_MASK(header, id);
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00837)
Packit 90a5c9
                 "socache_shmcb_remove (0x%02x -> subcache %d)",
Packit 90a5c9
                 SHMCB_MASK_DBG(header, id));
Packit 90a5c9
    if (idlen < 4) {
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00838) "unusably short id provided "
Packit 90a5c9
                "(%u bytes)", idlen);
Packit 90a5c9
        return APR_EINVAL;
Packit 90a5c9
    }
Packit 90a5c9
    if (shmcb_subcache_remove(s, header, subcache, id, idlen) == 0) {
Packit 90a5c9
        header->stat_removes_hit++;
Packit 90a5c9
        rv = APR_SUCCESS;
Packit 90a5c9
    } else {
Packit 90a5c9
        header->stat_removes_miss++;
Packit 90a5c9
        rv = APR_NOTFOUND;
Packit 90a5c9
    }
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00839)
Packit 90a5c9
                 "leaving socache_shmcb_remove successfully");
Packit 90a5c9
Packit 90a5c9
    return rv;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void socache_shmcb_status(ap_socache_instance_t *ctx,
Packit 90a5c9
                                 request_rec *r, int flags)
Packit 90a5c9
{
Packit 90a5c9
    server_rec *s = r->server;
Packit 90a5c9
    SHMCBHeader *header = ctx->header;
Packit 90a5c9
    unsigned int loop, total = 0, cache_total = 0, non_empty_subcaches = 0;
Packit 90a5c9
    apr_time_t idx_expiry, min_expiry = 0, max_expiry = 0;
Packit 90a5c9
    apr_time_t now = apr_time_now();
Packit 90a5c9
    double expiry_total = 0;
Packit 90a5c9
    int index_pct, cache_pct;
Packit 90a5c9
Packit 90a5c9
    AP_DEBUG_ASSERT(header->subcache_num > 0);
Packit 90a5c9
    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00840) "inside shmcb_status");
Packit 90a5c9
    /* Perform the iteration inside the mutex to avoid corruption or invalid
Packit 90a5c9
     * pointer arithmetic. The rest of our logic uses read-only header data so
Packit 90a5c9
     * doesn't need the lock. */
Packit 90a5c9
    /* Iterate over the subcaches */
Packit 90a5c9
    for (loop = 0; loop < header->subcache_num; loop++) {
Packit 90a5c9
        SHMCBSubcache *subcache = SHMCB_SUBCACHE(header, loop);
Packit 90a5c9
        shmcb_subcache_expire(s, header, subcache, now);
Packit 90a5c9
        total += subcache->idx_used;
Packit 90a5c9
        cache_total += subcache->data_used;
Packit 90a5c9
        if (subcache->idx_used) {
Packit 90a5c9
            SHMCBIndex *idx = SHMCB_INDEX(subcache, subcache->idx_pos);
Packit 90a5c9
            non_empty_subcaches++;
Packit 90a5c9
            idx_expiry = idx->expires;
Packit 90a5c9
            expiry_total += (double)idx_expiry;
Packit 90a5c9
            max_expiry = ((idx_expiry > max_expiry) ? idx_expiry : max_expiry);
Packit 90a5c9
            if (!min_expiry)
Packit 90a5c9
                min_expiry = idx_expiry;
Packit 90a5c9
            else
Packit 90a5c9
                min_expiry = ((idx_expiry < min_expiry) ? idx_expiry : min_expiry);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    index_pct = (100 * total) / (header->index_num *
Packit 90a5c9
                                 header->subcache_num);
Packit 90a5c9
    cache_pct = (100 * cache_total) / (header->subcache_data_size *
Packit 90a5c9
                                       header->subcache_num);
Packit 90a5c9
    /* Generate Output */
Packit 90a5c9
    if (!(flags & AP_STATUS_SHORT)) {
Packit 90a5c9
        ap_rprintf(r, "cache type: SHMCB, shared memory: %" APR_SIZE_T_FMT " "
Packit 90a5c9
                   "bytes, current entries: %d
",
Packit 90a5c9
                   ctx->shm_size, total);
Packit 90a5c9
        ap_rprintf(r, "subcaches: %d, indexes per subcache: %d
",
Packit 90a5c9
                   header->subcache_num, header->index_num);
Packit 90a5c9
        if (non_empty_subcaches) {
Packit 90a5c9
            apr_time_t average_expiry = (apr_time_t)(expiry_total / (double)non_empty_subcaches);
Packit 90a5c9
            ap_rprintf(r, "time left on oldest entries' objects: ");
Packit 90a5c9
            if (now < average_expiry)
Packit 90a5c9
                ap_rprintf(r, "avg: %d seconds, (range: %d...%d)
",
Packit 90a5c9
                           (int)apr_time_sec(average_expiry - now),
Packit 90a5c9
                           (int)apr_time_sec(min_expiry - now),
Packit 90a5c9
                           (int)apr_time_sec(max_expiry - now));
Packit 90a5c9
            else
Packit 90a5c9
                ap_rprintf(r, "expiry_threshold: Calculation error!
");
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        ap_rprintf(r, "index usage: %d%%, cache usage: %d%%
",
Packit 90a5c9
                   index_pct, cache_pct);
Packit 90a5c9
        ap_rprintf(r, "total entries stored since starting: %lu
",
Packit 90a5c9
                   header->stat_stores);
Packit 90a5c9
        ap_rprintf(r, "total entries replaced since starting: %lu
",
Packit 90a5c9
                   header->stat_replaced);
Packit 90a5c9
        ap_rprintf(r, "total entries expired since starting: %lu
",
Packit 90a5c9
                   header->stat_expiries);
Packit 90a5c9
        ap_rprintf(r, "total (pre-expiry) entries scrolled out of the cache: "
Packit 90a5c9
                   "%lu
", header->stat_scrolled);
Packit 90a5c9
        ap_rprintf(r, "total retrieves since starting: %lu hit, "
Packit 90a5c9
                   "%lu miss
", header->stat_retrieves_hit,
Packit 90a5c9
                   header->stat_retrieves_miss);
Packit 90a5c9
        ap_rprintf(r, "total removes since starting: %lu hit, "
Packit 90a5c9
                   "%lu miss
", header->stat_removes_hit,
Packit 90a5c9
                   header->stat_removes_miss);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        ap_rputs("CacheType: SHMCB\n", r);
Packit 90a5c9
        ap_rprintf(r, "CacheSharedMemory: %" APR_SIZE_T_FMT "\n",
Packit 90a5c9
                   ctx->shm_size);
Packit 90a5c9
        ap_rprintf(r, "CacheCurrentEntries: %d\n", total);
Packit 90a5c9
        ap_rprintf(r, "CacheSubcaches: %d\n", header->subcache_num);
Packit 90a5c9
        ap_rprintf(r, "CacheIndexesPerSubcaches: %d\n", header->index_num);
Packit 90a5c9
        if (non_empty_subcaches) {
Packit 90a5c9
            apr_time_t average_expiry = (apr_time_t)(expiry_total / (double)non_empty_subcaches);
Packit 90a5c9
            if (now < average_expiry) {
Packit 90a5c9
                ap_rprintf(r, "CacheTimeLeftOldestAvg: %d\n", (int)apr_time_sec(average_expiry - now));
Packit 90a5c9
                ap_rprintf(r, "CacheTimeLeftOldestMin: %d\n", (int)apr_time_sec(min_expiry - now));
Packit 90a5c9
                ap_rprintf(r, "CacheTimeLeftOldestMax: %d\n", (int)apr_time_sec(max_expiry - now));
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        ap_rprintf(r, "CacheIndexUsage: %d%%\n", index_pct);
Packit 90a5c9
        ap_rprintf(r, "CacheUsage: %d%%\n", cache_pct);
Packit 90a5c9
        ap_rprintf(r, "CacheStoreCount: %lu\n", header->stat_stores);
Packit 90a5c9
        ap_rprintf(r, "CacheReplaceCount: %lu\n", header->stat_replaced);
Packit 90a5c9
        ap_rprintf(r, "CacheExpireCount: %lu\n", header->stat_expiries);
Packit 90a5c9
        ap_rprintf(r, "CacheDiscardCount: %lu\n", header->stat_scrolled);
Packit 90a5c9
        ap_rprintf(r, "CacheRetrieveHitCount: %lu\n", header->stat_retrieves_hit);
Packit 90a5c9
        ap_rprintf(r, "CacheRetrieveMissCount: %lu\n", header->stat_retrieves_miss);
Packit 90a5c9
        ap_rprintf(r, "CacheRemoveHitCount: %lu\n", header->stat_removes_hit);
Packit 90a5c9
        ap_rprintf(r, "CacheRemoveMissCount: %lu\n", header->stat_removes_miss);
Packit 90a5c9
    }
Packit 90a5c9
    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00841) "leaving shmcb_status");
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static apr_status_t socache_shmcb_iterate(ap_socache_instance_t *instance,
Packit 90a5c9
                                          server_rec *s, void *userctx,
Packit 90a5c9
                                          ap_socache_iterator_t *iterator,
Packit 90a5c9
                                          apr_pool_t *pool)
Packit 90a5c9
{
Packit 90a5c9
    SHMCBHeader *header = instance->header;
Packit 90a5c9
    unsigned int loop;
Packit 90a5c9
    apr_time_t now = apr_time_now();
Packit 90a5c9
    apr_status_t rv = APR_SUCCESS;
Packit 90a5c9
    apr_size_t buflen = 0;
Packit 90a5c9
    unsigned char *buf = NULL;
Packit 90a5c9
Packit 90a5c9
    /* Perform the iteration inside the mutex to avoid corruption or invalid
Packit 90a5c9
     * pointer arithmetic. The rest of our logic uses read-only header data so
Packit 90a5c9
     * doesn't need the lock. */
Packit 90a5c9
    /* Iterate over the subcaches */
Packit 90a5c9
    for (loop = 0; loop < header->subcache_num && rv == APR_SUCCESS; loop++) {
Packit 90a5c9
        SHMCBSubcache *subcache = SHMCB_SUBCACHE(header, loop);
Packit 90a5c9
        rv = shmcb_subcache_iterate(instance, s, userctx, header, subcache,
Packit 90a5c9
                                    iterator, &buf, &buflen, pool, now);
Packit 90a5c9
    }
Packit 90a5c9
    return rv;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Subcache-level cache operations
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
static void shmcb_subcache_expire(server_rec *s, SHMCBHeader *header,
Packit 90a5c9
                                  SHMCBSubcache *subcache, apr_time_t now)
Packit 90a5c9
{
Packit 90a5c9
    unsigned int loop = 0, freed = 0, expired = 0;
Packit 90a5c9
    unsigned int new_idx_pos = subcache->idx_pos;
Packit 90a5c9
    SHMCBIndex *idx = NULL;
Packit 90a5c9
Packit 90a5c9
    while (loop < subcache->idx_used) {
Packit 90a5c9
        idx = SHMCB_INDEX(subcache, new_idx_pos);
Packit 90a5c9
        if (idx->removed)
Packit 90a5c9
            freed++;
Packit 90a5c9
        else if (idx->expires <= now)
Packit 90a5c9
            expired++;
Packit 90a5c9
        else
Packit 90a5c9
            /* not removed and not expired yet, we're done iterating */
Packit 90a5c9
            break;
Packit 90a5c9
        loop++;
Packit 90a5c9
        new_idx_pos = SHMCB_CYCLIC_INCREMENT(new_idx_pos, 1, header->index_num);
Packit 90a5c9
    }
Packit 90a5c9
    if (!loop)
Packit 90a5c9
        /* Nothing to do */
Packit 90a5c9
        return;
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00842)
Packit 90a5c9
                 "expiring %u and reclaiming %u removed socache entries",
Packit 90a5c9
                 expired, freed);
Packit 90a5c9
    if (loop == subcache->idx_used) {
Packit 90a5c9
        /* We're expiring everything, piece of cake */
Packit 90a5c9
        subcache->idx_used = 0;
Packit 90a5c9
        subcache->data_used = 0;
Packit 90a5c9
    } else {
Packit 90a5c9
        /* There remain other indexes, so we can use idx to adjust 'data' */
Packit 90a5c9
        unsigned int diff = SHMCB_CYCLIC_SPACE(subcache->data_pos,
Packit 90a5c9
                                               idx->data_pos,
Packit 90a5c9
                                               header->subcache_data_size);
Packit 90a5c9
        /* Adjust the indexes */
Packit 90a5c9
        subcache->idx_used -= loop;
Packit 90a5c9
        subcache->idx_pos = new_idx_pos;
Packit 90a5c9
        /* Adjust the data area */
Packit 90a5c9
        subcache->data_used -= diff;
Packit 90a5c9
        subcache->data_pos = idx->data_pos;
Packit 90a5c9
    }
Packit 90a5c9
    header->stat_expiries += expired;
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00843)
Packit 90a5c9
                 "we now have %u socache entries", subcache->idx_used);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int shmcb_subcache_store(server_rec *s, SHMCBHeader *header,
Packit 90a5c9
                                SHMCBSubcache *subcache,
Packit 90a5c9
                                unsigned char *data, unsigned int data_len,
Packit 90a5c9
                                const unsigned char *id, unsigned int id_len,
Packit 90a5c9
                                apr_time_t expiry)
Packit 90a5c9
{
Packit 90a5c9
    unsigned int data_offset, new_idx, id_offset;
Packit 90a5c9
    SHMCBIndex *idx;
Packit 90a5c9
    unsigned int total_len = id_len + data_len;
Packit 90a5c9
Packit 90a5c9
    /* Sanity check the input */
Packit 90a5c9
    if (total_len > header->subcache_data_size) {
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00844)
Packit 90a5c9
                     "inserting socache entry larger (%d) than subcache data area (%d)",
Packit 90a5c9
                     total_len, header->subcache_data_size);
Packit 90a5c9
        return -1;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* First reclaim space from removed and expired records. */
Packit 90a5c9
    shmcb_subcache_expire(s, header, subcache, apr_time_now());
Packit 90a5c9
Packit 90a5c9
    /* Loop until there is enough space to insert
Packit 90a5c9
     * XXX: This should first compress out-of-order expiries and
Packit 90a5c9
     * removed records, and then force-remove oldest-first
Packit 90a5c9
     */
Packit 90a5c9
    if (header->subcache_data_size - subcache->data_used < total_len
Packit 90a5c9
        || subcache->idx_used == header->index_num) {
Packit 90a5c9
        unsigned int loop = 0;
Packit 90a5c9
Packit 90a5c9
        idx = SHMCB_INDEX(subcache, subcache->idx_pos);
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00845)
Packit 90a5c9
                     "about to force-expire, subcache: idx_used=%d, "
Packit 90a5c9
                     "data_used=%d", subcache->idx_used, subcache->data_used);
Packit 90a5c9
        do {
Packit 90a5c9
            SHMCBIndex *idx2;
Packit 90a5c9
Packit 90a5c9
            /* Adjust the indexes by one */
Packit 90a5c9
            subcache->idx_pos = SHMCB_CYCLIC_INCREMENT(subcache->idx_pos, 1,
Packit 90a5c9
                                                       header->index_num);
Packit 90a5c9
            subcache->idx_used--;
Packit 90a5c9
            if (!subcache->idx_used) {
Packit 90a5c9
                /* There's nothing left */
Packit 90a5c9
                subcache->data_used = 0;
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
            /* Adjust the data */
Packit 90a5c9
            idx2 = SHMCB_INDEX(subcache, subcache->idx_pos);
Packit 90a5c9
            subcache->data_used -= SHMCB_CYCLIC_SPACE(idx->data_pos, idx2->data_pos,
Packit 90a5c9
                                                      header->subcache_data_size);
Packit 90a5c9
            subcache->data_pos = idx2->data_pos;
Packit 90a5c9
            /* Stats */
Packit 90a5c9
            header->stat_scrolled++;
Packit 90a5c9
            /* Loop admin */
Packit 90a5c9
            idx = idx2;
Packit 90a5c9
            loop++;
Packit 90a5c9
        } while (header->subcache_data_size - subcache->data_used < total_len);
Packit 90a5c9
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00846)
Packit 90a5c9
                     "finished force-expire, subcache: idx_used=%d, "
Packit 90a5c9
                     "data_used=%d", subcache->idx_used, subcache->data_used);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* HERE WE ASSUME THAT THE NEW ENTRY SHOULD GO ON THE END! I'M NOT
Packit 90a5c9
     * CHECKING WHETHER IT SHOULD BE GENUINELY "INSERTED" SOMEWHERE.
Packit 90a5c9
     *
Packit 90a5c9
     * We aught to fix that.  httpd (never mind third party modules)
Packit 90a5c9
     * does not promise to perform any processing in date order
Packit 90a5c9
     * (c.f. FAQ "My log entries are not in date order!")
Packit 90a5c9
     */
Packit 90a5c9
    /* Insert the id */
Packit 90a5c9
    id_offset = SHMCB_CYCLIC_INCREMENT(subcache->data_pos, subcache->data_used,
Packit 90a5c9
                                       header->subcache_data_size);
Packit 90a5c9
    shmcb_cyclic_ntoc_memcpy(header->subcache_data_size,
Packit 90a5c9
                             SHMCB_DATA(header, subcache), id_offset,
Packit 90a5c9
                             id, id_len);
Packit 90a5c9
    subcache->data_used += id_len;
Packit 90a5c9
    /* Insert the data */
Packit 90a5c9
    data_offset = SHMCB_CYCLIC_INCREMENT(subcache->data_pos, subcache->data_used,
Packit 90a5c9
                                         header->subcache_data_size);
Packit 90a5c9
    shmcb_cyclic_ntoc_memcpy(header->subcache_data_size,
Packit 90a5c9
                             SHMCB_DATA(header, subcache), data_offset,
Packit 90a5c9
                             data, data_len);
Packit 90a5c9
    subcache->data_used += data_len;
Packit 90a5c9
    /* Insert the index */
Packit 90a5c9
    new_idx = SHMCB_CYCLIC_INCREMENT(subcache->idx_pos, subcache->idx_used,
Packit 90a5c9
                                     header->index_num);
Packit 90a5c9
    idx = SHMCB_INDEX(subcache, new_idx);
Packit 90a5c9
    idx->expires = expiry;
Packit 90a5c9
    idx->data_pos = id_offset;
Packit 90a5c9
    idx->data_used = total_len;
Packit 90a5c9
    idx->id_len = id_len;
Packit 90a5c9
    idx->removed = 0;
Packit 90a5c9
    subcache->idx_used++;
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00847)
Packit 90a5c9
                 "insert happened at idx=%d, data=(%u:%u)", new_idx,
Packit 90a5c9
                 id_offset, data_offset);
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00848)
Packit 90a5c9
                 "finished insert, subcache: idx_pos/idx_used=%d/%d, "
Packit 90a5c9
                 "data_pos/data_used=%d/%d",
Packit 90a5c9
                 subcache->idx_pos, subcache->idx_used,
Packit 90a5c9
                 subcache->data_pos, subcache->data_used);
Packit 90a5c9
    return 0;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int shmcb_subcache_retrieve(server_rec *s, SHMCBHeader *header,
Packit 90a5c9
                                   SHMCBSubcache *subcache,
Packit 90a5c9
                                   const unsigned char *id, unsigned int idlen,
Packit 90a5c9
                                   unsigned char *dest, unsigned int *destlen)
Packit 90a5c9
{
Packit 90a5c9
    unsigned int pos;
Packit 90a5c9
    unsigned int loop = 0;
Packit 90a5c9
    apr_time_t now = apr_time_now();
Packit 90a5c9
Packit 90a5c9
    pos = subcache->idx_pos;
Packit 90a5c9
Packit 90a5c9
    while (loop < subcache->idx_used) {
Packit 90a5c9
        SHMCBIndex *idx = SHMCB_INDEX(subcache, pos);
Packit 90a5c9
Packit 90a5c9
        /* Only consider 'idx' if the id matches, and the "removed"
Packit 90a5c9
         * flag isn't set, and the record is not expired.
Packit 90a5c9
         * Check the data length too to avoid a buffer overflow
Packit 90a5c9
         * in case of corruption, which should be impossible,
Packit 90a5c9
         * but it's cheap to be safe. */
Packit 90a5c9
        if (!idx->removed
Packit 90a5c9
            && idx->id_len == idlen
Packit 90a5c9
            && (idx->data_used - idx->id_len) <= *destlen
Packit 90a5c9
            && shmcb_cyclic_memcmp(header->subcache_data_size,
Packit 90a5c9
                                   SHMCB_DATA(header, subcache),
Packit 90a5c9
                                   idx->data_pos, id, idx->id_len) == 0) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00849)
Packit 90a5c9
                         "match at idx=%d, data=%d", pos, idx->data_pos);
Packit 90a5c9
            if (idx->expires > now) {
Packit 90a5c9
                unsigned int data_offset;
Packit 90a5c9
Packit 90a5c9
                /* Find the offset of the data segment, after the id */
Packit 90a5c9
                data_offset = SHMCB_CYCLIC_INCREMENT(idx->data_pos,
Packit 90a5c9
                                                     idx->id_len,
Packit 90a5c9
                                                     header->subcache_data_size);
Packit 90a5c9
Packit 90a5c9
                *destlen = idx->data_used - idx->id_len;
Packit 90a5c9
Packit 90a5c9
                /* Copy out the data */
Packit 90a5c9
                shmcb_cyclic_cton_memcpy(header->subcache_data_size,
Packit 90a5c9
                                         dest, SHMCB_DATA(header, subcache),
Packit 90a5c9
                                         data_offset, *destlen);
Packit 90a5c9
Packit 90a5c9
                return 0;
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                /* Already stale, quietly remove and treat as not-found */
Packit 90a5c9
                idx->removed = 1;
Packit 90a5c9
                header->stat_expiries++;
Packit 90a5c9
                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00850)
Packit 90a5c9
                             "shmcb_subcache_retrieve discarding expired entry");
Packit 90a5c9
                return -1;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        /* Increment */
Packit 90a5c9
        loop++;
Packit 90a5c9
        pos = SHMCB_CYCLIC_INCREMENT(pos, 1, header->index_num);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00851)
Packit 90a5c9
                 "shmcb_subcache_retrieve found no match");
Packit 90a5c9
    return -1;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int shmcb_subcache_remove(server_rec *s, SHMCBHeader *header,
Packit 90a5c9
                                 SHMCBSubcache *subcache,
Packit 90a5c9
                                 const unsigned char *id,
Packit 90a5c9
                                 unsigned int idlen)
Packit 90a5c9
{
Packit 90a5c9
    unsigned int pos;
Packit 90a5c9
    unsigned int loop = 0;
Packit 90a5c9
Packit 90a5c9
    pos = subcache->idx_pos;
Packit 90a5c9
    while (loop < subcache->idx_used) {
Packit 90a5c9
        SHMCBIndex *idx = SHMCB_INDEX(subcache, pos);
Packit 90a5c9
Packit 90a5c9
        /* Only consider 'idx' if the id matches, and the "removed"
Packit 90a5c9
         * flag isn't set. */
Packit 90a5c9
        if (!idx->removed && idx->id_len == idlen
Packit 90a5c9
            && shmcb_cyclic_memcmp(header->subcache_data_size,
Packit 90a5c9
                                   SHMCB_DATA(header, subcache),
Packit 90a5c9
                                   idx->data_pos, id, idx->id_len) == 0) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00852)
Packit 90a5c9
                         "possible match at idx=%d, data=%d", pos, idx->data_pos);
Packit 90a5c9
Packit 90a5c9
            /* Found the matching entry, remove it quietly. */
Packit 90a5c9
            idx->removed = 1;
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00853)
Packit 90a5c9
                         "shmcb_subcache_remove removing matching entry");
Packit 90a5c9
            return 0;
Packit 90a5c9
        }
Packit 90a5c9
        /* Increment */
Packit 90a5c9
        loop++;
Packit 90a5c9
        pos = SHMCB_CYCLIC_INCREMENT(pos, 1, header->index_num);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return -1; /* failure */
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
static apr_status_t shmcb_subcache_iterate(ap_socache_instance_t *instance,
Packit 90a5c9
                                           server_rec *s,
Packit 90a5c9
                                           void *userctx,
Packit 90a5c9
                                           SHMCBHeader *header,
Packit 90a5c9
                                           SHMCBSubcache *subcache,
Packit 90a5c9
                                           ap_socache_iterator_t *iterator,
Packit 90a5c9
                                           unsigned char **buf,
Packit 90a5c9
                                           apr_size_t *buf_len,
Packit 90a5c9
                                           apr_pool_t *pool,
Packit 90a5c9
                                           apr_time_t now)
Packit 90a5c9
{
Packit 90a5c9
    unsigned int pos;
Packit 90a5c9
    unsigned int loop = 0;
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
Packit 90a5c9
    pos = subcache->idx_pos;
Packit 90a5c9
    while (loop < subcache->idx_used) {
Packit 90a5c9
        SHMCBIndex *idx = SHMCB_INDEX(subcache, pos);
Packit 90a5c9
Packit 90a5c9
        /* Only consider 'idx' if the "removed" flag isn't set. */
Packit 90a5c9
        if (!idx->removed) {
Packit 90a5c9
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00854)
Packit 90a5c9
                         "iterating idx=%d, data=%d", pos, idx->data_pos);
Packit 90a5c9
            if (idx->expires > now) {
Packit 90a5c9
                unsigned char *id = *buf;
Packit 90a5c9
                unsigned char *dest;
Packit 90a5c9
                unsigned int data_offset, dest_len;
Packit 90a5c9
                apr_size_t buf_req;
Packit 90a5c9
Packit 90a5c9
                /* Find the offset of the data segment, after the id */
Packit 90a5c9
                data_offset = SHMCB_CYCLIC_INCREMENT(idx->data_pos,
Packit 90a5c9
                                                     idx->id_len,
Packit 90a5c9
                                                     header->subcache_data_size);
Packit 90a5c9
Packit 90a5c9
                dest_len = idx->data_used - idx->id_len;
Packit 90a5c9
Packit 90a5c9
                buf_req = APR_ALIGN_DEFAULT(idx->id_len + 1)
Packit 90a5c9
                        + APR_ALIGN_DEFAULT(dest_len + 1);
Packit 90a5c9
Packit 90a5c9
                if (buf_req > *buf_len) {
Packit 90a5c9
                     /* Grow to ~150% of this buffer requirement on resize
Packit 90a5c9
                      * always using APR_ALIGN_DEFAULT sized pages
Packit 90a5c9
                      */
Packit 90a5c9
                     *buf_len = buf_req + APR_ALIGN_DEFAULT(buf_req / 2);
Packit 90a5c9
                     *buf = apr_palloc(pool, *buf_len);
Packit 90a5c9
                     id = *buf;
Packit 90a5c9
                }
Packit 90a5c9
Packit 90a5c9
                dest = *buf + APR_ALIGN_DEFAULT(idx->id_len + 1);
Packit 90a5c9
Packit 90a5c9
                /* Copy out the data, because it's potentially cyclic */
Packit 90a5c9
                shmcb_cyclic_cton_memcpy(header->subcache_data_size, id,
Packit 90a5c9
                                         SHMCB_DATA(header, subcache),
Packit 90a5c9
                                         idx->data_pos, idx->id_len);
Packit 90a5c9
                id[idx->id_len] = '\0';
Packit 90a5c9
Packit 90a5c9
                shmcb_cyclic_cton_memcpy(header->subcache_data_size, dest,
Packit 90a5c9
                                         SHMCB_DATA(header, subcache),
Packit 90a5c9
                                         data_offset, dest_len);
Packit 90a5c9
                dest[dest_len] = '\0';
Packit 90a5c9
Packit 90a5c9
                rv = iterator(instance, s, userctx, id, idx->id_len,
Packit 90a5c9
                              dest, dest_len, pool);
Packit 90a5c9
                ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(00855)
Packit 90a5c9
                             "shmcb entry iterated");
Packit 90a5c9
                if (rv != APR_SUCCESS)
Packit 90a5c9
                    return rv;
Packit 90a5c9
            }
Packit 90a5c9
            else {
Packit 90a5c9
                /* Already stale, quietly remove and treat as not-found */
Packit 90a5c9
                idx->removed = 1;
Packit 90a5c9
                header->stat_expiries++;
Packit 90a5c9
                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00856)
Packit 90a5c9
                             "shmcb_subcache_iterate discarding expired entry");
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        /* Increment */
Packit 90a5c9
        loop++;
Packit 90a5c9
        pos = SHMCB_CYCLIC_INCREMENT(pos, 1, header->index_num);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const ap_socache_provider_t socache_shmcb = {
Packit 90a5c9
    "shmcb",
Packit 90a5c9
    AP_SOCACHE_FLAG_NOTMPSAFE,
Packit 90a5c9
    socache_shmcb_create,
Packit 90a5c9
    socache_shmcb_init,
Packit 90a5c9
    socache_shmcb_destroy,
Packit 90a5c9
    socache_shmcb_store,
Packit 90a5c9
    socache_shmcb_retrieve,
Packit 90a5c9
    socache_shmcb_remove,
Packit 90a5c9
    socache_shmcb_status,
Packit 90a5c9
    socache_shmcb_iterate
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
static void register_hooks(apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP, "shmcb",
Packit 90a5c9
                         AP_SOCACHE_PROVIDER_VERSION,
Packit 90a5c9
                         &socache_shmcb);
Packit 90a5c9
Packit 90a5c9
    /* Also register shmcb under the default provider name. */
Packit 90a5c9
    ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP,
Packit 90a5c9
                         AP_SOCACHE_DEFAULT_PROVIDER,
Packit 90a5c9
                         AP_SOCACHE_PROVIDER_VERSION,
Packit 90a5c9
                         &socache_shmcb);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(socache_shmcb) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    NULL, NULL, NULL, NULL, NULL,
Packit 90a5c9
    register_hooks
Packit 90a5c9
};