2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2014 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Sascha Schumann <sascha@schumann.cx> |
16 +----------------------------------------------------------------------+
29 #include <sys/types.h>
32 #include "php_session.h"
37 # error mm is not thread-safe
40 #define PS_MM_FILE "session_mm_"
43 #include "ext/standard/basic_functions.h"
45 /* This list holds all data associated with one session. */
47 typedef struct ps_sd
{
49 php_uint32 hv
; /* hash value of key */
50 time_t ctime
; /* time of last change */
52 size_t datalen
; /* amount of valid data */
53 size_t alloclen
; /* amount of allocated memory for data */
54 char key
[1]; /* inline key */
65 static ps_mm
*ps_mm_instance
= NULL
;
68 # define ps_mm_debug(a) printf a
70 # define ps_mm_debug(a)
73 static inline php_uint32
ps_sd_hash(const char *data
, int len
)
76 const char *e
= data
+ len
;
78 for (h
= 2166136261U; data
< e
; ) {
86 static void hash_split(ps_mm
*data
)
90 ps_sd
**ohash
, **ehash
;
93 nmax
= ((data
->hash_max
+ 1) << 1) - 1;
94 nhash
= mm_calloc(data
->mm
, nmax
+ 1, sizeof(*data
->hash
));
97 /* no further memory to expand hash table */
101 ehash
= data
->hash
+ data
->hash_max
+ 1;
102 for (ohash
= data
->hash
; ohash
< ehash
; ohash
++) {
103 for (ps
= *ohash
; ps
; ps
= next
) {
105 ps
->next
= nhash
[ps
->hv
& nmax
];
106 nhash
[ps
->hv
& nmax
] = ps
;
109 mm_free(data
->mm
, data
->hash
);
112 data
->hash_max
= nmax
;
115 static ps_sd
*ps_sd_new(ps_mm
*data
, const char *key
)
121 keylen
= strlen(key
);
123 sd
= mm_malloc(data
->mm
, sizeof(ps_sd
) + keylen
);
127 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "mm_malloc failed, avail %ld, err %s", mm_available(data
->mm
), mm_error());
131 hv
= ps_sd_hash(key
, keylen
);
132 slot
= hv
& data
->hash_max
;
137 sd
->alloclen
= sd
->datalen
= 0;
139 memcpy(sd
->key
, key
, keylen
+ 1);
141 sd
->next
= data
->hash
[slot
];
142 data
->hash
[slot
] = sd
;
147 if (data
->hash_cnt
>= data
->hash_max
) {
152 ps_mm_debug(("inserting %s(%p) into slot %d\n", key
, sd
, slot
));
157 static void ps_sd_destroy(ps_mm
*data
, ps_sd
*sd
)
161 slot
= ps_sd_hash(sd
->key
, strlen(sd
->key
)) & data
->hash_max
;
163 if (data
->hash
[slot
] == sd
) {
164 data
->hash
[slot
] = sd
->next
;
168 /* There must be some entry before the one we want to delete */
169 for (prev
= data
->hash
[slot
]; prev
->next
!= sd
; prev
= prev
->next
);
170 prev
->next
= sd
->next
;
176 mm_free(data
->mm
, sd
->data
);
179 mm_free(data
->mm
, sd
);
182 static ps_sd
*ps_sd_lookup(ps_mm
*data
, const char *key
, int rw
)
187 hv
= ps_sd_hash(key
, strlen(key
));
188 slot
= hv
& data
->hash_max
;
190 for (prev
= NULL
, ret
= data
->hash
[slot
]; ret
; prev
= ret
, ret
= ret
->next
) {
191 if (ret
->hv
== hv
&& !strcmp(ret
->key
, key
)) {
196 if (ret
&& rw
&& ret
!= data
->hash
[slot
]) {
197 /* Move the entry to the top of the linked list */
199 prev
->next
= ret
->next
;
202 ret
->next
= data
->hash
[slot
];
203 data
->hash
[slot
] = ret
;
206 ps_mm_debug(("lookup(%s): ret=%p,hv=%u,slot=%d\n", key
, ret
, hv
, slot
));
211 static int ps_mm_key_exists(ps_mm
*data
, const char *key TSRMLS_DC
)
218 sd
= ps_sd_lookup(data
, key
, 0);
225 ps_module ps_mod_mm
= {
229 #define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA()
231 static int ps_mm_initialize(ps_mm
*data
, const char *path
)
233 data
->owner
= getpid();
234 data
->mm
= mm_create(0, path
);
240 data
->hash_max
= 511;
241 data
->hash
= mm_calloc(data
->mm
, data
->hash_max
+ 1, sizeof(ps_sd
*));
243 mm_destroy(data
->mm
);
250 static void ps_mm_destroy(ps_mm
*data
)
255 /* This function is called during each module shutdown,
256 but we must not release the shared memory pool, when
257 an Apache child dies! */
258 if (data
->owner
!= getpid()) {
262 for (h
= 0; h
< data
->hash_max
+ 1; h
++) {
263 for (sd
= data
->hash
[h
]; sd
; sd
= next
) {
265 ps_sd_destroy(data
, sd
);
269 mm_free(data
->mm
, data
->hash
);
270 mm_destroy(data
->mm
);
274 PHP_MINIT_FUNCTION(ps_mm
)
276 int save_path_len
= strlen(PS(save_path
));
277 int mod_name_len
= strlen(sapi_module
.name
);
279 char *ps_mm_path
, euid
[30];
282 ps_mm_instance
= calloc(sizeof(*ps_mm_instance
), 1);
283 if (!ps_mm_instance
) {
287 if (!(euid_len
= slprintf(euid
, sizeof(euid
), "%d", geteuid()))) {
288 free(ps_mm_instance
);
289 ps_mm_instance
= NULL
;
293 /* Directory + '/' + File + Module Name + Effective UID + \0 */
294 ps_mm_path
= emalloc(save_path_len
+ 1 + (sizeof(PS_MM_FILE
) - 1) + mod_name_len
+ euid_len
+ 1);
296 memcpy(ps_mm_path
, PS(save_path
), save_path_len
);
297 if (save_path_len
&& PS(save_path
)[save_path_len
- 1] != DEFAULT_SLASH
) {
298 ps_mm_path
[save_path_len
] = DEFAULT_SLASH
;
301 memcpy(ps_mm_path
+ save_path_len
, PS_MM_FILE
, sizeof(PS_MM_FILE
) - 1);
302 save_path_len
+= sizeof(PS_MM_FILE
) - 1;
303 memcpy(ps_mm_path
+ save_path_len
, sapi_module
.name
, mod_name_len
);
304 save_path_len
+= mod_name_len
;
305 memcpy(ps_mm_path
+ save_path_len
, euid
, euid_len
);
306 ps_mm_path
[save_path_len
+ euid_len
] = '\0';
308 ret
= ps_mm_initialize(ps_mm_instance
, ps_mm_path
);
312 if (ret
!= SUCCESS
) {
313 free(ps_mm_instance
);
314 ps_mm_instance
= NULL
;
318 php_session_register_module(&ps_mod_mm
);
322 PHP_MSHUTDOWN_FUNCTION(ps_mm
)
324 if (ps_mm_instance
) {
325 ps_mm_destroy(ps_mm_instance
);
333 ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance
));
335 if (!ps_mm_instance
) {
338 PS_SET_MOD_DATA(ps_mm_instance
);
345 PS_SET_MOD_DATA(NULL
);
356 mm_lock(data
->mm
, MM_LOCK_RD
);
358 /* If there is an ID and strict mode, verify existence */
359 if (PS(use_strict_mode
)
360 && ps_mm_key_exists(data
, key TSRMLS_CC
) == FAILURE
) {
361 /* key points to PS(id), but cannot change here. */
366 PS(id
) = PS(mod
)->s_create_sid((void **)&data
, NULL TSRMLS_CC
);
370 if (PS(use_cookies
)) {
373 php_session_reset_id(TSRMLS_C
);
374 PS(session_status
) = php_session_active
;
377 sd
= ps_sd_lookup(data
, PS(id
), 0);
379 *vallen
= sd
->datalen
;
380 *val
= emalloc(sd
->datalen
+ 1);
381 memcpy(*val
, sd
->data
, sd
->datalen
);
382 (*val
)[sd
->datalen
] = '\0';
396 mm_lock(data
->mm
, MM_LOCK_RW
);
398 sd
= ps_sd_lookup(data
, key
, 1);
400 sd
= ps_sd_new(data
, key
);
401 ps_mm_debug(("new entry for %s\n", key
));
405 if (vallen
>= sd
->alloclen
) {
407 mm_free(data
->mm
, sd
->data
);
409 sd
->alloclen
= vallen
+ 1;
410 sd
->data
= mm_malloc(data
->mm
, sd
->alloclen
);
413 ps_sd_destroy(data
, sd
);
414 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "cannot allocate new data segment");
419 sd
->datalen
= vallen
;
420 memcpy(sd
->data
, val
, vallen
);
427 return sd
? SUCCESS
: FAILURE
;
435 mm_lock(data
->mm
, MM_LOCK_RW
);
437 sd
= ps_sd_lookup(data
, key
, 0);
439 ps_sd_destroy(data
, sd
);
451 ps_sd
**ohash
, **ehash
;
455 ps_mm_debug(("gc\n"));
459 limit
-= maxlifetime
;
461 mm_lock(data
->mm
, MM_LOCK_RW
);
463 ehash
= data
->hash
+ data
->hash_max
+ 1;
464 for (ohash
= data
->hash
; ohash
< ehash
; ohash
++) {
465 for (sd
= *ohash
; sd
; sd
= next
) {
467 if (sd
->ctime
< limit
) {
468 ps_mm_debug(("purging %s\n", sd
->key
));
469 ps_sd_destroy(data
, sd
);
480 PS_CREATE_SID_FUNC(mm
)
487 sid
= php_session_create_id((void **)&data
, newlen TSRMLS_CC
);
488 /* Check collision */
489 if (ps_mm_key_exists(data
, sid TSRMLS_CC
) == SUCCESS
) {
510 * vim600: sw=4 ts=4 fdm=marker