Annotation of embedaddon/php/ext/session/mod_mm.c, revision 1.1.1.5

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:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>