Back to index

php5  5.3.10
mod_mm.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Sascha Schumann <sascha@schumann.cx>                         |
00016    +----------------------------------------------------------------------+
00017  */
00018 
00019 /* $Id: mod_mm.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #include "php.h"
00022 
00023 #ifdef HAVE_LIBMM
00024 
00025 #include <unistd.h>
00026 #include <mm.h>
00027 #include <time.h>
00028 #include <sys/stat.h>
00029 #include <sys/types.h>
00030 #include <fcntl.h>
00031 
00032 #include "php_session.h"
00033 #include "mod_mm.h"
00034 #include "SAPI.h"
00035 
00036 #ifdef ZTS
00037 # error mm is not thread-safe
00038 #endif
00039 
00040 #define PS_MM_FILE "session_mm_"
00041 
00042 /* For php_uint32 */
00043 #include "ext/standard/basic_functions.h"
00044 
00045 /* This list holds all data associated with one session. */
00046 
00047 typedef struct ps_sd {
00048        struct ps_sd *next;
00049        php_uint32 hv;              /* hash value of key */
00050        time_t ctime;        /* time of last change */
00051        void *data;
00052        size_t datalen;             /* amount of valid data */
00053        size_t alloclen;     /* amount of allocated memory for data */
00054        char key[1];         /* inline key */
00055 } ps_sd;
00056 
00057 typedef struct {
00058        MM *mm;
00059        ps_sd **hash;
00060        php_uint32 hash_max;
00061        php_uint32 hash_cnt;
00062        pid_t owner;
00063 } ps_mm;
00064 
00065 static ps_mm *ps_mm_instance = NULL;
00066 
00067 #if 0
00068 # define ps_mm_debug(a) printf a
00069 #else
00070 # define ps_mm_debug(a)
00071 #endif
00072 
00073 static inline php_uint32 ps_sd_hash(const char *data, int len)
00074 {
00075        php_uint32 h;
00076        const char *e = data + len;
00077 
00078        for (h = 2166136261U; data < e; ) {
00079               h *= 16777619;
00080               h ^= *data++;
00081        }
00082 
00083        return h;
00084 }
00085 
00086 static void hash_split(ps_mm *data)
00087 {
00088        php_uint32 nmax;
00089        ps_sd **nhash;
00090        ps_sd **ohash, **ehash;
00091        ps_sd *ps, *next;
00092 
00093        nmax = ((data->hash_max + 1) << 1) - 1;
00094        nhash = mm_calloc(data->mm, nmax + 1, sizeof(*data->hash));
00095 
00096        if (!nhash) {
00097               /* no further memory to expand hash table */
00098               return;
00099        }
00100 
00101        ehash = data->hash + data->hash_max + 1;
00102        for (ohash = data->hash; ohash < ehash; ohash++) {
00103               for (ps = *ohash; ps; ps = next) {
00104                      next = ps->next;
00105                      ps->next = nhash[ps->hv & nmax];
00106                      nhash[ps->hv & nmax] = ps;
00107               }
00108        }
00109        mm_free(data->mm, data->hash);
00110 
00111        data->hash = nhash;
00112        data->hash_max = nmax;
00113 }
00114 
00115 static ps_sd *ps_sd_new(ps_mm *data, const char *key)
00116 {
00117        php_uint32 hv, slot;
00118        ps_sd *sd;
00119        int keylen;
00120 
00121        keylen = strlen(key);
00122 
00123        sd = mm_malloc(data->mm, sizeof(ps_sd) + keylen);
00124        if (!sd) {
00125               TSRMLS_FETCH();
00126 
00127               php_error_docref(NULL TSRMLS_CC, E_WARNING, "mm_malloc failed, avail %d, err %s", mm_available(data->mm), mm_error());
00128               return NULL;
00129        }
00130 
00131        hv = ps_sd_hash(key, keylen);
00132        slot = hv & data->hash_max;
00133 
00134        sd->ctime = 0;
00135        sd->hv = hv;
00136        sd->data = NULL;
00137        sd->alloclen = sd->datalen = 0;
00138 
00139        memcpy(sd->key, key, keylen + 1);
00140 
00141        sd->next = data->hash[slot];
00142        data->hash[slot] = sd;
00143 
00144        data->hash_cnt++;
00145 
00146        if (!sd->next) {
00147               if (data->hash_cnt >= data->hash_max) {
00148                      hash_split(data);
00149               }
00150        }
00151 
00152        ps_mm_debug(("inserting %s(%p) into slot %d\n", key, sd, slot));
00153 
00154        return sd;
00155 }
00156 
00157 static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
00158 {
00159        php_uint32 slot;
00160 
00161        slot = ps_sd_hash(sd->key, strlen(sd->key)) & data->hash_max;
00162 
00163        if (data->hash[slot] == sd) {
00164               data->hash[slot] = sd->next;
00165        } else {
00166               ps_sd *prev;
00167 
00168               /* There must be some entry before the one we want to delete */
00169               for (prev = data->hash[slot]; prev->next != sd; prev = prev->next);
00170               prev->next = sd->next;
00171        }
00172 
00173        data->hash_cnt--;
00174 
00175        if (sd->data) {
00176               mm_free(data->mm, sd->data);
00177        }
00178 
00179        mm_free(data->mm, sd);
00180 }
00181 
00182 static ps_sd *ps_sd_lookup(ps_mm *data, const char *key, int rw)
00183 {
00184        php_uint32 hv, slot;
00185        ps_sd *ret, *prev;
00186 
00187        hv = ps_sd_hash(key, strlen(key));
00188        slot = hv & data->hash_max;
00189 
00190        for (prev = NULL, ret = data->hash[slot]; ret; prev = ret, ret = ret->next) {
00191               if (ret->hv == hv && !strcmp(ret->key, key)) {
00192                      break;
00193               }
00194        }
00195 
00196        if (ret && rw && ret != data->hash[slot]) {
00197               /* Move the entry to the top of the linked list */
00198               if (prev) {
00199                      prev->next = ret->next;
00200               }
00201 
00202               ret->next = data->hash[slot];
00203               data->hash[slot] = ret;
00204        }
00205 
00206        ps_mm_debug(("lookup(%s): ret=%p,hv=%u,slot=%d\n", key, ret, hv, slot));
00207 
00208        return ret;
00209 }
00210 
00211 ps_module ps_mod_mm = {
00212        PS_MOD(mm)
00213 };
00214 
00215 #define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA()
00216 
00217 static int ps_mm_initialize(ps_mm *data, const char *path)
00218 {
00219        data->owner = getpid();
00220        data->mm = mm_create(0, path);
00221        if (!data->mm) {
00222               return FAILURE;
00223        }
00224 
00225        data->hash_cnt = 0;
00226        data->hash_max = 511;
00227        data->hash = mm_calloc(data->mm, data->hash_max + 1, sizeof(ps_sd *));
00228        if (!data->hash) {
00229               mm_destroy(data->mm);
00230               return FAILURE;
00231        }
00232 
00233        return SUCCESS;
00234 }
00235 
00236 static void ps_mm_destroy(ps_mm *data)
00237 {
00238        int h;
00239        ps_sd *sd, *next;
00240 
00241        /* This function is called during each module shutdown,
00242           but we must not release the shared memory pool, when
00243           an Apache child dies! */
00244        if (data->owner != getpid()) {
00245               return;
00246        }
00247 
00248        for (h = 0; h < data->hash_max + 1; h++) {
00249               for (sd = data->hash[h]; sd; sd = next) {
00250                      next = sd->next;
00251                      ps_sd_destroy(data, sd);
00252               }
00253        }
00254 
00255        mm_free(data->mm, data->hash);
00256        mm_destroy(data->mm);
00257        free(data);
00258 }
00259 
00260 PHP_MINIT_FUNCTION(ps_mm)
00261 {
00262        int save_path_len = strlen(PS(save_path));
00263        int mod_name_len = strlen(sapi_module.name);
00264        int euid_len;
00265        char *ps_mm_path, euid[30];
00266        int ret;
00267 
00268        ps_mm_instance = calloc(sizeof(*ps_mm_instance), 1);
00269        if (!ps_mm_instance) {
00270               return FAILURE;
00271        }
00272 
00273        if (!(euid_len = slprintf(euid, sizeof(euid), "%d", geteuid()))) {
00274               return FAILURE;
00275        }
00276 
00277        /* Directory + '/' + File + Module Name + Effective UID + \0 */
00278        ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1);
00279 
00280        memcpy(ps_mm_path, PS(save_path), save_path_len);
00281        if (save_path_len && PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) {
00282               ps_mm_path[save_path_len] = DEFAULT_SLASH;
00283               save_path_len++;
00284        }
00285        memcpy(ps_mm_path + save_path_len, PS_MM_FILE, sizeof(PS_MM_FILE) - 1);
00286        save_path_len += sizeof(PS_MM_FILE) - 1;
00287        memcpy(ps_mm_path + save_path_len, sapi_module.name, mod_name_len);
00288        save_path_len += mod_name_len;
00289        memcpy(ps_mm_path + save_path_len, euid, euid_len);
00290        ps_mm_path[save_path_len + euid_len] = '\0';
00291 
00292        ret = ps_mm_initialize(ps_mm_instance, ps_mm_path);
00293 
00294        efree(ps_mm_path);
00295 
00296        if (ret != SUCCESS) {
00297               free(ps_mm_instance);
00298               ps_mm_instance = NULL;
00299               return FAILURE;
00300        }
00301 
00302        php_session_register_module(&ps_mod_mm);
00303        return SUCCESS;
00304 }
00305 
00306 PHP_MSHUTDOWN_FUNCTION(ps_mm)
00307 {
00308        if (ps_mm_instance) {
00309               ps_mm_destroy(ps_mm_instance);
00310               return SUCCESS;
00311        }
00312        return FAILURE;
00313 }
00314 
00315 PS_OPEN_FUNC(mm)
00316 {
00317        ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance));
00318 
00319        if (!ps_mm_instance) {
00320               return FAILURE;
00321        }
00322        PS_SET_MOD_DATA(ps_mm_instance);
00323 
00324        return SUCCESS;
00325 }
00326 
00327 PS_CLOSE_FUNC(mm)
00328 {
00329        PS_SET_MOD_DATA(NULL);
00330 
00331        return SUCCESS;
00332 }
00333 
00334 PS_READ_FUNC(mm)
00335 {
00336        PS_MM_DATA;
00337        ps_sd *sd;
00338        int ret = FAILURE;
00339 
00340        mm_lock(data->mm, MM_LOCK_RD);
00341 
00342        sd = ps_sd_lookup(data, key, 0);
00343        if (sd) {
00344               *vallen = sd->datalen;
00345               *val = emalloc(sd->datalen + 1);
00346               memcpy(*val, sd->data, sd->datalen);
00347               (*val)[sd->datalen] = '\0';
00348               ret = SUCCESS;
00349        }
00350 
00351        mm_unlock(data->mm);
00352 
00353        return ret;
00354 }
00355 
00356 PS_WRITE_FUNC(mm)
00357 {
00358        PS_MM_DATA;
00359        ps_sd *sd;
00360 
00361        mm_lock(data->mm, MM_LOCK_RW);
00362 
00363        sd = ps_sd_lookup(data, key, 1);
00364        if (!sd) {
00365               sd = ps_sd_new(data, key);
00366               ps_mm_debug(("new entry for %s\n", key));
00367        }
00368 
00369        if (sd) {
00370               if (vallen >= sd->alloclen) {
00371                      if (data->mm) {
00372                             mm_free(data->mm, sd->data);
00373                      }
00374                      sd->alloclen = vallen + 1;
00375                      sd->data = mm_malloc(data->mm, sd->alloclen);
00376 
00377                      if (!sd->data) {
00378                             ps_sd_destroy(data, sd);
00379                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot allocate new data segment");
00380                             sd = NULL;
00381                      }
00382               }
00383               if (sd) {
00384                      sd->datalen = vallen;
00385                      memcpy(sd->data, val, vallen);
00386                      time(&sd->ctime);
00387               }
00388        }
00389 
00390        mm_unlock(data->mm);
00391 
00392        return sd ? SUCCESS : FAILURE;
00393 }
00394 
00395 PS_DESTROY_FUNC(mm)
00396 {
00397        PS_MM_DATA;
00398        ps_sd *sd;
00399 
00400        mm_lock(data->mm, MM_LOCK_RW);
00401 
00402        sd = ps_sd_lookup(data, key, 0);
00403        if (sd) {
00404               ps_sd_destroy(data, sd);
00405        }
00406 
00407        mm_unlock(data->mm);
00408 
00409        return SUCCESS;
00410 }
00411 
00412 PS_GC_FUNC(mm)
00413 {
00414        PS_MM_DATA;
00415        time_t limit;
00416        ps_sd **ohash, **ehash;
00417        ps_sd *sd, *next;
00418 
00419        *nrdels = 0;
00420        ps_mm_debug(("gc\n"));
00421 
00422        time(&limit);
00423 
00424        limit -= maxlifetime;
00425 
00426        mm_lock(data->mm, MM_LOCK_RW);
00427 
00428        ehash = data->hash + data->hash_max + 1;
00429        for (ohash = data->hash; ohash < ehash; ohash++) {
00430               for (sd = *ohash; sd; sd = next) {
00431                      next = sd->next;
00432                      if (sd->ctime < limit) {
00433                             ps_mm_debug(("purging %s\n", sd->key));
00434                             ps_sd_destroy(data, sd);
00435                             (*nrdels)++;
00436                      }
00437               }
00438        }
00439 
00440        mm_unlock(data->mm);
00441 
00442        return SUCCESS;
00443 }
00444 
00445 #endif
00446 
00447 /*
00448  * Local variables:
00449  * tab-width: 4
00450  * c-basic-offset: 4
00451  * End:
00452  * vim600: sw=4 ts=4 fdm=marker
00453  * vim<600: sw=4 ts=4
00454  */