Return to mod_mm.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / session |
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.5 ! misho 5: | Copyright (c) 1997-2014 The PHP Group |
1.1 misho 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: +----------------------------------------------------------------------+
17: */
18:
1.1.1.2 misho 19: /* $Id$ */
1.1 misho 20:
21: #include "php.h"
22:
23: #ifdef HAVE_LIBMM
24:
25: #include <unistd.h>
26: #include <mm.h>
27: #include <time.h>
28: #include <sys/stat.h>
29: #include <sys/types.h>
30: #include <fcntl.h>
31:
32: #include "php_session.h"
33: #include "mod_mm.h"
34: #include "SAPI.h"
35:
36: #ifdef ZTS
37: # error mm is not thread-safe
38: #endif
39:
40: #define PS_MM_FILE "session_mm_"
41:
42: /* For php_uint32 */
43: #include "ext/standard/basic_functions.h"
44:
45: /* This list holds all data associated with one session. */
46:
47: typedef struct ps_sd {
48: struct ps_sd *next;
49: php_uint32 hv; /* hash value of key */
50: time_t ctime; /* time of last change */
51: void *data;
52: size_t datalen; /* amount of valid data */
53: size_t alloclen; /* amount of allocated memory for data */
54: char key[1]; /* inline key */
55: } ps_sd;
56:
57: typedef struct {
58: MM *mm;
59: ps_sd **hash;
60: php_uint32 hash_max;
61: php_uint32 hash_cnt;
62: pid_t owner;
63: } ps_mm;
64:
65: static ps_mm *ps_mm_instance = NULL;
66:
67: #if 0
68: # define ps_mm_debug(a) printf a
69: #else
70: # define ps_mm_debug(a)
71: #endif
72:
73: static inline php_uint32 ps_sd_hash(const char *data, int len)
74: {
75: php_uint32 h;
76: const char *e = data + len;
77:
78: for (h = 2166136261U; data < e; ) {
79: h *= 16777619;
80: h ^= *data++;
81: }
82:
83: return h;
84: }
85:
86: static void hash_split(ps_mm *data)
87: {
88: php_uint32 nmax;
89: ps_sd **nhash;
90: ps_sd **ohash, **ehash;
91: ps_sd *ps, *next;
92:
93: nmax = ((data->hash_max + 1) << 1) - 1;
94: nhash = mm_calloc(data->mm, nmax + 1, sizeof(*data->hash));
95:
96: if (!nhash) {
97: /* no further memory to expand hash table */
98: return;
99: }
100:
101: ehash = data->hash + data->hash_max + 1;
102: for (ohash = data->hash; ohash < ehash; ohash++) {
103: for (ps = *ohash; ps; ps = next) {
104: next = ps->next;
105: ps->next = nhash[ps->hv & nmax];
106: nhash[ps->hv & nmax] = ps;
107: }
108: }
109: mm_free(data->mm, data->hash);
110:
111: data->hash = nhash;
112: data->hash_max = nmax;
113: }
114:
115: static ps_sd *ps_sd_new(ps_mm *data, const char *key)
116: {
117: php_uint32 hv, slot;
118: ps_sd *sd;
119: int keylen;
120:
121: keylen = strlen(key);
122:
123: sd = mm_malloc(data->mm, sizeof(ps_sd) + keylen);
124: if (!sd) {
125: TSRMLS_FETCH();
126:
127: php_error_docref(NULL TSRMLS_CC, E_WARNING, "mm_malloc failed, avail %d, err %s", mm_available(data->mm), mm_error());
128: return NULL;
129: }
130:
131: hv = ps_sd_hash(key, keylen);
132: slot = hv & data->hash_max;
133:
134: sd->ctime = 0;
135: sd->hv = hv;
136: sd->data = NULL;
137: sd->alloclen = sd->datalen = 0;
138:
139: memcpy(sd->key, key, keylen + 1);
140:
141: sd->next = data->hash[slot];
142: data->hash[slot] = sd;
143:
144: data->hash_cnt++;
145:
146: if (!sd->next) {
147: if (data->hash_cnt >= data->hash_max) {
148: hash_split(data);
149: }
150: }
151:
152: ps_mm_debug(("inserting %s(%p) into slot %d\n", key, sd, slot));
153:
154: return sd;
155: }
156:
157: static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
158: {
159: php_uint32 slot;
160:
161: slot = ps_sd_hash(sd->key, strlen(sd->key)) & data->hash_max;
162:
163: if (data->hash[slot] == sd) {
164: data->hash[slot] = sd->next;
165: } else {
166: ps_sd *prev;
167:
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;
171: }
172:
173: data->hash_cnt--;
174:
175: if (sd->data) {
176: mm_free(data->mm, sd->data);
177: }
178:
179: mm_free(data->mm, sd);
180: }
181:
182: static ps_sd *ps_sd_lookup(ps_mm *data, const char *key, int rw)
183: {
184: php_uint32 hv, slot;
185: ps_sd *ret, *prev;
186:
187: hv = ps_sd_hash(key, strlen(key));
188: slot = hv & data->hash_max;
189:
190: for (prev = NULL, ret = data->hash[slot]; ret; prev = ret, ret = ret->next) {
191: if (ret->hv == hv && !strcmp(ret->key, key)) {
192: break;
193: }
194: }
195:
196: if (ret && rw && ret != data->hash[slot]) {
197: /* Move the entry to the top of the linked list */
198: if (prev) {
199: prev->next = ret->next;
200: }
201:
202: ret->next = data->hash[slot];
203: data->hash[slot] = ret;
204: }
205:
206: ps_mm_debug(("lookup(%s): ret=%p,hv=%u,slot=%d\n", key, ret, hv, slot));
207:
208: return ret;
209: }
210:
211: ps_module ps_mod_mm = {
212: PS_MOD(mm)
213: };
214:
215: #define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA()
216:
217: static int ps_mm_initialize(ps_mm *data, const char *path)
218: {
219: data->owner = getpid();
220: data->mm = mm_create(0, path);
221: if (!data->mm) {
222: return FAILURE;
223: }
224:
225: data->hash_cnt = 0;
226: data->hash_max = 511;
227: data->hash = mm_calloc(data->mm, data->hash_max + 1, sizeof(ps_sd *));
228: if (!data->hash) {
229: mm_destroy(data->mm);
230: return FAILURE;
231: }
232:
233: return SUCCESS;
234: }
235:
236: static void ps_mm_destroy(ps_mm *data)
237: {
238: int h;
239: ps_sd *sd, *next;
240:
241: /* This function is called during each module shutdown,
242: but we must not release the shared memory pool, when
243: an Apache child dies! */
244: if (data->owner != getpid()) {
245: return;
246: }
247:
248: for (h = 0; h < data->hash_max + 1; h++) {
249: for (sd = data->hash[h]; sd; sd = next) {
250: next = sd->next;
251: ps_sd_destroy(data, sd);
252: }
253: }
254:
255: mm_free(data->mm, data->hash);
256: mm_destroy(data->mm);
257: free(data);
258: }
259:
260: PHP_MINIT_FUNCTION(ps_mm)
261: {
262: int save_path_len = strlen(PS(save_path));
263: int mod_name_len = strlen(sapi_module.name);
264: int euid_len;
265: char *ps_mm_path, euid[30];
266: int ret;
267:
268: ps_mm_instance = calloc(sizeof(*ps_mm_instance), 1);
269: if (!ps_mm_instance) {
270: return FAILURE;
271: }
272:
273: if (!(euid_len = slprintf(euid, sizeof(euid), "%d", geteuid()))) {
1.1.1.4 misho 274: free(ps_mm_instance);
275: ps_mm_instance = NULL;
1.1 misho 276: return FAILURE;
277: }
278:
279: /* Directory + '/' + File + Module Name + Effective UID + \0 */
280: ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1);
281:
282: memcpy(ps_mm_path, PS(save_path), save_path_len);
283: if (save_path_len && PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) {
284: ps_mm_path[save_path_len] = DEFAULT_SLASH;
285: save_path_len++;
286: }
287: memcpy(ps_mm_path + save_path_len, PS_MM_FILE, sizeof(PS_MM_FILE) - 1);
288: save_path_len += sizeof(PS_MM_FILE) - 1;
289: memcpy(ps_mm_path + save_path_len, sapi_module.name, mod_name_len);
290: save_path_len += mod_name_len;
291: memcpy(ps_mm_path + save_path_len, euid, euid_len);
292: ps_mm_path[save_path_len + euid_len] = '\0';
293:
294: ret = ps_mm_initialize(ps_mm_instance, ps_mm_path);
295:
296: efree(ps_mm_path);
297:
298: if (ret != SUCCESS) {
299: free(ps_mm_instance);
300: ps_mm_instance = NULL;
301: return FAILURE;
302: }
303:
304: php_session_register_module(&ps_mod_mm);
305: return SUCCESS;
306: }
307:
308: PHP_MSHUTDOWN_FUNCTION(ps_mm)
309: {
310: if (ps_mm_instance) {
311: ps_mm_destroy(ps_mm_instance);
312: return SUCCESS;
313: }
314: return FAILURE;
315: }
316:
317: PS_OPEN_FUNC(mm)
318: {
319: ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance));
320:
321: if (!ps_mm_instance) {
322: return FAILURE;
323: }
324: PS_SET_MOD_DATA(ps_mm_instance);
325:
326: return SUCCESS;
327: }
328:
329: PS_CLOSE_FUNC(mm)
330: {
331: PS_SET_MOD_DATA(NULL);
332:
333: return SUCCESS;
334: }
335:
336: PS_READ_FUNC(mm)
337: {
338: PS_MM_DATA;
339: ps_sd *sd;
340: int ret = FAILURE;
341:
342: mm_lock(data->mm, MM_LOCK_RD);
343:
344: sd = ps_sd_lookup(data, key, 0);
345: if (sd) {
346: *vallen = sd->datalen;
347: *val = emalloc(sd->datalen + 1);
348: memcpy(*val, sd->data, sd->datalen);
349: (*val)[sd->datalen] = '\0';
350: ret = SUCCESS;
351: }
352:
353: mm_unlock(data->mm);
354:
355: return ret;
356: }
357:
358: PS_WRITE_FUNC(mm)
359: {
360: PS_MM_DATA;
361: ps_sd *sd;
362:
363: mm_lock(data->mm, MM_LOCK_RW);
364:
365: sd = ps_sd_lookup(data, key, 1);
366: if (!sd) {
367: sd = ps_sd_new(data, key);
368: ps_mm_debug(("new entry for %s\n", key));
369: }
370:
371: if (sd) {
372: if (vallen >= sd->alloclen) {
373: if (data->mm) {
374: mm_free(data->mm, sd->data);
375: }
376: sd->alloclen = vallen + 1;
377: sd->data = mm_malloc(data->mm, sd->alloclen);
378:
379: if (!sd->data) {
380: ps_sd_destroy(data, sd);
381: php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot allocate new data segment");
382: sd = NULL;
383: }
384: }
385: if (sd) {
386: sd->datalen = vallen;
387: memcpy(sd->data, val, vallen);
388: time(&sd->ctime);
389: }
390: }
391:
392: mm_unlock(data->mm);
393:
394: return sd ? SUCCESS : FAILURE;
395: }
396:
397: PS_DESTROY_FUNC(mm)
398: {
399: PS_MM_DATA;
400: ps_sd *sd;
401:
402: mm_lock(data->mm, MM_LOCK_RW);
403:
404: sd = ps_sd_lookup(data, key, 0);
405: if (sd) {
406: ps_sd_destroy(data, sd);
407: }
408:
409: mm_unlock(data->mm);
410:
411: return SUCCESS;
412: }
413:
414: PS_GC_FUNC(mm)
415: {
416: PS_MM_DATA;
417: time_t limit;
418: ps_sd **ohash, **ehash;
419: ps_sd *sd, *next;
420:
421: *nrdels = 0;
422: ps_mm_debug(("gc\n"));
423:
424: time(&limit);
425:
426: limit -= maxlifetime;
427:
428: mm_lock(data->mm, MM_LOCK_RW);
429:
430: ehash = data->hash + data->hash_max + 1;
431: for (ohash = data->hash; ohash < ehash; ohash++) {
432: for (sd = *ohash; sd; sd = next) {
433: next = sd->next;
434: if (sd->ctime < limit) {
435: ps_mm_debug(("purging %s\n", sd->key));
436: ps_sd_destroy(data, sd);
437: (*nrdels)++;
438: }
439: }
440: }
441:
442: mm_unlock(data->mm);
443:
444: return SUCCESS;
445: }
446:
447: #endif
448:
449: /*
450: * Local variables:
451: * tab-width: 4
452: * c-basic-offset: 4
453: * End:
454: * vim600: sw=4 ts=4 fdm=marker
455: * vim<600: sw=4 ts=4
456: */