Annotation of embedaddon/php/ext/session/mod_mm.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2012 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: +----------------------------------------------------------------------+
! 17: */
! 18:
! 19: /* $Id: mod_mm.c 321634 2012-01-01 13:15:04Z felipe $ */
! 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()))) {
! 274: return FAILURE;
! 275: }
! 276:
! 277: /* Directory + '/' + File + Module Name + Effective UID + \0 */
! 278: ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1);
! 279:
! 280: memcpy(ps_mm_path, PS(save_path), save_path_len);
! 281: if (save_path_len && PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) {
! 282: ps_mm_path[save_path_len] = DEFAULT_SLASH;
! 283: save_path_len++;
! 284: }
! 285: memcpy(ps_mm_path + save_path_len, PS_MM_FILE, sizeof(PS_MM_FILE) - 1);
! 286: save_path_len += sizeof(PS_MM_FILE) - 1;
! 287: memcpy(ps_mm_path + save_path_len, sapi_module.name, mod_name_len);
! 288: save_path_len += mod_name_len;
! 289: memcpy(ps_mm_path + save_path_len, euid, euid_len);
! 290: ps_mm_path[save_path_len + euid_len] = '\0';
! 291:
! 292: ret = ps_mm_initialize(ps_mm_instance, ps_mm_path);
! 293:
! 294: efree(ps_mm_path);
! 295:
! 296: if (ret != SUCCESS) {
! 297: free(ps_mm_instance);
! 298: ps_mm_instance = NULL;
! 299: return FAILURE;
! 300: }
! 301:
! 302: php_session_register_module(&ps_mod_mm);
! 303: return SUCCESS;
! 304: }
! 305:
! 306: PHP_MSHUTDOWN_FUNCTION(ps_mm)
! 307: {
! 308: if (ps_mm_instance) {
! 309: ps_mm_destroy(ps_mm_instance);
! 310: return SUCCESS;
! 311: }
! 312: return FAILURE;
! 313: }
! 314:
! 315: PS_OPEN_FUNC(mm)
! 316: {
! 317: ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance));
! 318:
! 319: if (!ps_mm_instance) {
! 320: return FAILURE;
! 321: }
! 322: PS_SET_MOD_DATA(ps_mm_instance);
! 323:
! 324: return SUCCESS;
! 325: }
! 326:
! 327: PS_CLOSE_FUNC(mm)
! 328: {
! 329: PS_SET_MOD_DATA(NULL);
! 330:
! 331: return SUCCESS;
! 332: }
! 333:
! 334: PS_READ_FUNC(mm)
! 335: {
! 336: PS_MM_DATA;
! 337: ps_sd *sd;
! 338: int ret = FAILURE;
! 339:
! 340: mm_lock(data->mm, MM_LOCK_RD);
! 341:
! 342: sd = ps_sd_lookup(data, key, 0);
! 343: if (sd) {
! 344: *vallen = sd->datalen;
! 345: *val = emalloc(sd->datalen + 1);
! 346: memcpy(*val, sd->data, sd->datalen);
! 347: (*val)[sd->datalen] = '\0';
! 348: ret = SUCCESS;
! 349: }
! 350:
! 351: mm_unlock(data->mm);
! 352:
! 353: return ret;
! 354: }
! 355:
! 356: PS_WRITE_FUNC(mm)
! 357: {
! 358: PS_MM_DATA;
! 359: ps_sd *sd;
! 360:
! 361: mm_lock(data->mm, MM_LOCK_RW);
! 362:
! 363: sd = ps_sd_lookup(data, key, 1);
! 364: if (!sd) {
! 365: sd = ps_sd_new(data, key);
! 366: ps_mm_debug(("new entry for %s\n", key));
! 367: }
! 368:
! 369: if (sd) {
! 370: if (vallen >= sd->alloclen) {
! 371: if (data->mm) {
! 372: mm_free(data->mm, sd->data);
! 373: }
! 374: sd->alloclen = vallen + 1;
! 375: sd->data = mm_malloc(data->mm, sd->alloclen);
! 376:
! 377: if (!sd->data) {
! 378: ps_sd_destroy(data, sd);
! 379: php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot allocate new data segment");
! 380: sd = NULL;
! 381: }
! 382: }
! 383: if (sd) {
! 384: sd->datalen = vallen;
! 385: memcpy(sd->data, val, vallen);
! 386: time(&sd->ctime);
! 387: }
! 388: }
! 389:
! 390: mm_unlock(data->mm);
! 391:
! 392: return sd ? SUCCESS : FAILURE;
! 393: }
! 394:
! 395: PS_DESTROY_FUNC(mm)
! 396: {
! 397: PS_MM_DATA;
! 398: ps_sd *sd;
! 399:
! 400: mm_lock(data->mm, MM_LOCK_RW);
! 401:
! 402: sd = ps_sd_lookup(data, key, 0);
! 403: if (sd) {
! 404: ps_sd_destroy(data, sd);
! 405: }
! 406:
! 407: mm_unlock(data->mm);
! 408:
! 409: return SUCCESS;
! 410: }
! 411:
! 412: PS_GC_FUNC(mm)
! 413: {
! 414: PS_MM_DATA;
! 415: time_t limit;
! 416: ps_sd **ohash, **ehash;
! 417: ps_sd *sd, *next;
! 418:
! 419: *nrdels = 0;
! 420: ps_mm_debug(("gc\n"));
! 421:
! 422: time(&limit);
! 423:
! 424: limit -= maxlifetime;
! 425:
! 426: mm_lock(data->mm, MM_LOCK_RW);
! 427:
! 428: ehash = data->hash + data->hash_max + 1;
! 429: for (ohash = data->hash; ohash < ehash; ohash++) {
! 430: for (sd = *ohash; sd; sd = next) {
! 431: next = sd->next;
! 432: if (sd->ctime < limit) {
! 433: ps_mm_debug(("purging %s\n", sd->key));
! 434: ps_sd_destroy(data, sd);
! 435: (*nrdels)++;
! 436: }
! 437: }
! 438: }
! 439:
! 440: mm_unlock(data->mm);
! 441:
! 442: return SUCCESS;
! 443: }
! 444:
! 445: #endif
! 446:
! 447: /*
! 448: * Local variables:
! 449: * tab-width: 4
! 450: * c-basic-offset: 4
! 451: * End:
! 452: * vim600: sw=4 ts=4 fdm=marker
! 453: * vim<600: sw=4 ts=4
! 454: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>