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>