bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / ssl / ssl_scache_shmht.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*                      _             _
18  *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
19  * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
20  * | | | | | | (_) | (_| |   \__ \__ \ |
21  * |_| |_| |_|\___/ \__,_|___|___/___/_|
22  *                      |_____|
23  *  ssl_scache_shmht.c
24  *  Session Cache via Shared Memory (Hash Table Variant)
25  */
26
27 #include "mod_ssl.h"
28
29 /*
30  *  Wrapper functions for table library which resemble malloc(3) & Co 
31  *  but use the variants from the MM shared memory library.
32  */
33
34 static void *ssl_scache_shmht_malloc(void *opt_param, size_t size)
35 {
36     SSLModConfigRec *mc = myModConfig((server_rec *)opt_param);
37
38     apr_rmm_off_t off = apr_rmm_calloc(mc->pSessionCacheDataRMM, size);
39     return apr_rmm_addr_get(mc->pSessionCacheDataRMM, off);
40 }
41
42 static void *ssl_scache_shmht_calloc(void *opt_param,
43                                      size_t number, size_t size)
44 {
45     SSLModConfigRec *mc = myModConfig((server_rec *)opt_param);
46
47     apr_rmm_off_t off = apr_rmm_calloc(mc->pSessionCacheDataRMM, (number*size));
48
49     return apr_rmm_addr_get(mc->pSessionCacheDataRMM, off);
50 }
51
52 static void *ssl_scache_shmht_realloc(void *opt_param, void *ptr, size_t size)
53 {
54     SSLModConfigRec *mc = myModConfig((server_rec *)opt_param);
55
56     apr_rmm_off_t off = apr_rmm_realloc(mc->pSessionCacheDataRMM, ptr, size);
57     return apr_rmm_addr_get(mc->pSessionCacheDataRMM, off);
58 }
59
60 static void ssl_scache_shmht_free(void *opt_param, void *ptr)
61 {
62     SSLModConfigRec *mc = myModConfig((server_rec *)opt_param);
63
64     apr_rmm_off_t off = apr_rmm_offset_get(mc->pSessionCacheDataRMM, ptr);
65     apr_rmm_free(mc->pSessionCacheDataRMM, off);
66     return;
67 }
68
69 /*
70  * Now the actual session cache implementation
71  * based on a hash table inside a shared memory segment.
72  */
73
74 void ssl_scache_shmht_init(server_rec *s, apr_pool_t *p)
75 {
76     SSLModConfigRec *mc = myModConfig(s);
77     table_t *ta;
78     int ta_errno;
79     apr_size_t avail;
80     int n;
81     apr_status_t rv;
82
83     /*
84      * Create shared memory segment
85      */
86     if (mc->szSessionCacheDataFile == NULL) {
87         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
88                      "SSLSessionCache required");
89         ssl_die();
90     }
91
92     if ((rv = apr_shm_create(&(mc->pSessionCacheDataMM), 
93                    mc->nSessionCacheDataSize, 
94                    mc->szSessionCacheDataFile, mc->pPool)) != APR_SUCCESS) {
95         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
96                      "Cannot allocate shared memory");
97         ssl_die();
98     }
99
100     if ((rv = apr_rmm_init(&(mc->pSessionCacheDataRMM), NULL,
101                    apr_shm_baseaddr_get(mc->pSessionCacheDataMM),
102                    mc->nSessionCacheDataSize, mc->pPool)) != APR_SUCCESS) {
103         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
104                      "Cannot initialize rmm");
105         ssl_die();
106     }
107     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
108                  "initialize MM %pp RMM %pp",
109                  mc->pSessionCacheDataMM, mc->pSessionCacheDataRMM);
110
111     /*
112      * Create hash table in shared memory segment
113      */
114     avail = mc->nSessionCacheDataSize;
115     n = (avail/2) / 1024;
116     n = n < 10 ? 10 : n;
117
118     /*
119      * Passing server_rec as opt_param to table_alloc so that we can do
120      * logging if required ssl_util_table. Otherwise, mc is sufficient.
121      */ 
122     if ((ta = table_alloc(n, &ta_errno, 
123                           ssl_scache_shmht_malloc,  
124                           ssl_scache_shmht_calloc, 
125                           ssl_scache_shmht_realloc, 
126                           ssl_scache_shmht_free, s )) == NULL) {
127         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
128                      "Cannot allocate hash table in shared memory: %s",
129                      table_strerror(ta_errno));
130         ssl_die();
131     }
132
133     table_attr(ta, TABLE_FLAG_AUTO_ADJUST|TABLE_FLAG_ADJUST_DOWN);
134     table_set_data_alignment(ta, sizeof(char *));
135     table_clear(ta);
136     mc->tSessionCacheDataTable = ta;
137
138     /*
139      * Log the done work
140      */
141     ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, 
142                  "Init: Created hash-table (%d buckets) "
143                  "in shared memory (%" APR_SIZE_T_FMT 
144                  " bytes) for SSL session cache",
145                  n, avail);
146     return;
147 }
148
149 void ssl_scache_shmht_kill(server_rec *s)
150 {
151     SSLModConfigRec *mc = myModConfig(s);
152
153     if (mc->pSessionCacheDataRMM != NULL) {
154         apr_rmm_destroy(mc->pSessionCacheDataRMM);
155         mc->pSessionCacheDataRMM = NULL;
156     }
157
158     if (mc->pSessionCacheDataMM != NULL) {
159         apr_shm_destroy(mc->pSessionCacheDataMM);
160         mc->pSessionCacheDataMM = NULL;
161     }
162     return;
163 }
164
165 BOOL ssl_scache_shmht_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
166 {
167     SSLModConfigRec *mc = myModConfig(s);
168     void *vp;
169     UCHAR ucaData[SSL_SESSION_MAX_DER];
170     int nData;
171     UCHAR *ucp;
172
173     /* streamline session data */
174     if ((nData = i2d_SSL_SESSION(sess, NULL)) > sizeof(ucaData))
175         return FALSE;
176     ucp = ucaData;
177     i2d_SSL_SESSION(sess, &ucp);
178
179     ssl_mutex_on(s);
180     if (table_insert_kd(mc->tSessionCacheDataTable, 
181                         id, idlen, NULL, sizeof(time_t)+nData,
182                         NULL, &vp, 1) != TABLE_ERROR_NONE) {
183         ssl_mutex_off(s);
184         return FALSE;
185     }
186     memcpy(vp, &expiry, sizeof(time_t));
187     memcpy((char *)vp+sizeof(time_t), ucaData, nData);
188     ssl_mutex_off(s);
189
190     /* allow the regular expiring to occur */
191     ssl_scache_shmht_expire(s);
192
193     return TRUE;
194 }
195
196 SSL_SESSION *ssl_scache_shmht_retrieve(server_rec *s, UCHAR *id, int idlen)
197 {
198     SSLModConfigRec *mc = myModConfig(s);
199     void *vp;
200     SSL_SESSION *sess = NULL;
201     MODSSL_D2I_SSL_SESSION_CONST UCHAR *ucpData;
202     int nData;
203     time_t expiry;
204     time_t now;
205     int n;
206
207     /* allow the regular expiring to occur */
208     ssl_scache_shmht_expire(s);
209
210     /* lookup key in table */
211     ssl_mutex_on(s);
212     if (table_retrieve(mc->tSessionCacheDataTable,
213                        id, idlen, &vp, &n) != TABLE_ERROR_NONE) {
214         ssl_mutex_off(s);
215         return NULL;
216     }
217
218     /* copy over the information to the SCI */
219     nData = n-sizeof(time_t);
220     ucpData = (UCHAR *)malloc(nData);
221     if (ucpData == NULL) {
222         ssl_mutex_off(s);
223         return NULL;
224     }
225     memcpy(&expiry, vp, sizeof(time_t));
226     memcpy((void *)ucpData, (char *)vp+sizeof(time_t), nData);
227     ssl_mutex_off(s);
228
229     /* make sure the stuff is still not expired */
230     now = time(NULL);
231     if (expiry <= now) {
232         ssl_scache_shmht_remove(s, id, idlen);
233         return NULL;
234     }
235
236     /* unstreamed SSL_SESSION */
237     sess = d2i_SSL_SESSION(NULL, &ucpData, nData);
238
239     return sess;
240 }
241
242 void ssl_scache_shmht_remove(server_rec *s, UCHAR *id, int idlen)
243 {
244     SSLModConfigRec *mc = myModConfig(s);
245
246     /* remove value under key in table */
247     ssl_mutex_on(s);
248     table_delete(mc->tSessionCacheDataTable, id, idlen, NULL, NULL);
249     ssl_mutex_off(s);
250     return;
251 }
252
253 void ssl_scache_shmht_expire(server_rec *s)
254 {
255     SSLModConfigRec *mc = myModConfig(s);
256     SSLSrvConfigRec *sc = mySrvConfig(s);
257     static time_t tLast = 0;
258     table_linear_t iterator;
259     time_t tExpiresAt;
260     void *vpKey;
261     void *vpKeyThis;
262     void *vpData;
263     int nKey;
264     int nKeyThis;
265     int nData;
266     int nElements = 0;
267     int nDeleted = 0;
268     int bDelete;
269     int rc;
270     time_t tNow;
271
272     /*
273      * make sure the expiration for still not-accessed session
274      * cache entries is done only from time to time
275      */
276     tNow = time(NULL);
277     if (tNow < tLast+sc->session_cache_timeout)
278         return;
279     tLast = tNow;
280
281     ssl_mutex_on(s);
282     if (table_first_r(mc->tSessionCacheDataTable, &iterator,
283                       &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) {
284         do {
285             bDelete = FALSE;
286             nElements++;
287             if (nData < sizeof(time_t) || vpData == NULL)
288                 bDelete = TRUE;
289             else {
290                 memcpy(&tExpiresAt, vpData, sizeof(time_t));
291                 /* 
292                  * XXX : Force the record to be cleaned up. TBD (Madhu)
293                  * tExpiresAt = tNow;
294                  */
295                 if (tExpiresAt <= tNow)
296                    bDelete = TRUE;
297             }
298             vpKeyThis = vpKey;
299             nKeyThis  = nKey;
300             rc = table_next_r(mc->tSessionCacheDataTable, &iterator,
301                               &vpKey, &nKey, &vpData, &nData);
302             if (bDelete) {
303                 table_delete(mc->tSessionCacheDataTable,
304                              vpKeyThis, nKeyThis, NULL, NULL);
305                 nDeleted++;
306             }
307         } while (rc == TABLE_ERROR_NONE); 
308         /* (vpKeyThis != vpKey) && (nKeyThis != nKey) */
309     }
310     ssl_mutex_off(s);
311     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
312                  "Inter-Process Session Cache (SHMHT) Expiry: "
313                  "old: %d, new: %d, removed: %d",
314                  nElements, nElements-nDeleted, nDeleted);
315     return;
316 }
317
318 void ssl_scache_shmht_status(server_rec *s, apr_pool_t *p, void (*func)(char *, void *), void *arg)
319 {
320     SSLModConfigRec *mc = myModConfig(s);
321     void *vpKey;
322     void *vpData;
323     int nKey;
324     int nData;
325     int nElem;
326     int nSize;
327     int nAverage;
328
329     nElem = 0;
330     nSize = 0;
331     ssl_mutex_on(s);
332     if (table_first(mc->tSessionCacheDataTable,
333                     &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) {
334         do {
335             if (vpKey == NULL || vpData == NULL)
336                 continue;
337             nElem += 1;
338             nSize += nData;
339         } while (table_next(mc->tSessionCacheDataTable,
340                         &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE);
341     }
342     ssl_mutex_off(s);
343     if (nSize > 0 && nElem > 0)
344         nAverage = nSize / nElem;
345     else
346         nAverage = 0;
347     func(apr_psprintf(p, "cache type: <b>SHMHT</b>, maximum size: <b>%d</b> bytes<br>", mc->nSessionCacheDataSize), arg);
348     func(apr_psprintf(p, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize), arg);
349     func(apr_psprintf(p, "average session size: <b>%d</b> bytes<br>", nAverage), arg);
350     return;
351 }