root/ext/session/mod_mm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ps_sd_hash
  2. hash_split
  3. ps_sd_new
  4. ps_sd_destroy
  5. ps_sd_lookup
  6. ps_mm_key_exists
  7. ps_mm_initialize
  8. ps_mm_destroy
  9. PHP_MINIT_FUNCTION
  10. PHP_MSHUTDOWN_FUNCTION
  11. PS_OPEN_FUNC
  12. PS_CLOSE_FUNC
  13. PS_READ_FUNC
  14. PS_WRITE_FUNC
  15. PS_DESTROY_FUNC
  16. PS_GC_FUNC
  17. PS_CREATE_SID_FUNC

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 5                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 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$ */
  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 %ld, 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 static int ps_mm_key_exists(ps_mm *data, const char *key TSRMLS_DC)
 212 {
 213         ps_sd *sd;
 214 
 215         if (!key) {
 216                 return FAILURE;
 217         }
 218         sd = ps_sd_lookup(data, key, 0);
 219         if (sd) {
 220                 return SUCCESS;
 221         }
 222         return FAILURE;
 223 }
 224 
 225 ps_module ps_mod_mm = {
 226         PS_MOD_SID(mm)
 227 };
 228 
 229 #define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA()
 230 
 231 static int ps_mm_initialize(ps_mm *data, const char *path)
 232 {
 233         data->owner = getpid();
 234         data->mm = mm_create(0, path);
 235         if (!data->mm) {
 236                 return FAILURE;
 237         }
 238 
 239         data->hash_cnt = 0;
 240         data->hash_max = 511;
 241         data->hash = mm_calloc(data->mm, data->hash_max + 1, sizeof(ps_sd *));
 242         if (!data->hash) {
 243                 mm_destroy(data->mm);
 244                 return FAILURE;
 245         }
 246 
 247         return SUCCESS;
 248 }
 249 
 250 static void ps_mm_destroy(ps_mm *data)
 251 {
 252         int h;
 253         ps_sd *sd, *next;
 254 
 255         /* This function is called during each module shutdown,
 256            but we must not release the shared memory pool, when
 257            an Apache child dies! */
 258         if (data->owner != getpid()) {
 259                 return;
 260         }
 261 
 262         for (h = 0; h < data->hash_max + 1; h++) {
 263                 for (sd = data->hash[h]; sd; sd = next) {
 264                         next = sd->next;
 265                         ps_sd_destroy(data, sd);
 266                 }
 267         }
 268 
 269         mm_free(data->mm, data->hash);
 270         mm_destroy(data->mm);
 271         free(data);
 272 }
 273 
 274 PHP_MINIT_FUNCTION(ps_mm)
 275 {
 276         int save_path_len = strlen(PS(save_path));
 277         int mod_name_len = strlen(sapi_module.name);
 278         int euid_len;
 279         char *ps_mm_path, euid[30];
 280         int ret;
 281 
 282         ps_mm_instance = calloc(sizeof(*ps_mm_instance), 1);
 283         if (!ps_mm_instance) {
 284                 return FAILURE;
 285         }
 286 
 287         if (!(euid_len = slprintf(euid, sizeof(euid), "%d", geteuid()))) {
 288                 free(ps_mm_instance);
 289                 ps_mm_instance = NULL;
 290                 return FAILURE;
 291         }
 292 
 293         /* Directory + '/' + File + Module Name + Effective UID + \0 */
 294         ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1);
 295 
 296         memcpy(ps_mm_path, PS(save_path), save_path_len);
 297         if (save_path_len && PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) {
 298                 ps_mm_path[save_path_len] = DEFAULT_SLASH;
 299                 save_path_len++;
 300         }
 301         memcpy(ps_mm_path + save_path_len, PS_MM_FILE, sizeof(PS_MM_FILE) - 1);
 302         save_path_len += sizeof(PS_MM_FILE) - 1;
 303         memcpy(ps_mm_path + save_path_len, sapi_module.name, mod_name_len);
 304         save_path_len += mod_name_len;
 305         memcpy(ps_mm_path + save_path_len, euid, euid_len);
 306         ps_mm_path[save_path_len + euid_len] = '\0';
 307 
 308         ret = ps_mm_initialize(ps_mm_instance, ps_mm_path);
 309 
 310         efree(ps_mm_path);
 311 
 312         if (ret != SUCCESS) {
 313                 free(ps_mm_instance);
 314                 ps_mm_instance = NULL;
 315                 return FAILURE;
 316         }
 317 
 318         php_session_register_module(&ps_mod_mm);
 319         return SUCCESS;
 320 }
 321 
 322 PHP_MSHUTDOWN_FUNCTION(ps_mm)
 323 {
 324         if (ps_mm_instance) {
 325                 ps_mm_destroy(ps_mm_instance);
 326                 return SUCCESS;
 327         }
 328         return FAILURE;
 329 }
 330 
 331 PS_OPEN_FUNC(mm)
 332 {
 333         ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance));
 334 
 335         if (!ps_mm_instance) {
 336                 return FAILURE;
 337         }
 338         PS_SET_MOD_DATA(ps_mm_instance);
 339 
 340         return SUCCESS;
 341 }
 342 
 343 PS_CLOSE_FUNC(mm)
 344 {
 345         PS_SET_MOD_DATA(NULL);
 346 
 347         return SUCCESS;
 348 }
 349 
 350 PS_READ_FUNC(mm)
 351 {
 352         PS_MM_DATA;
 353         ps_sd *sd;
 354         int ret = FAILURE;
 355 
 356         mm_lock(data->mm, MM_LOCK_RD);
 357 
 358         /* If there is an ID and strict mode, verify existence */
 359         if (PS(use_strict_mode)
 360                 && ps_mm_key_exists(data, key TSRMLS_CC) == FAILURE) {
 361                 /* key points to PS(id), but cannot change here. */
 362                 if (key) {
 363                         efree(PS(id));
 364                         PS(id) = NULL;
 365                 }
 366                 PS(id) = PS(mod)->s_create_sid((void **)&data, NULL TSRMLS_CC);
 367                 if (!PS(id)) {
 368                         return FAILURE;
 369                 }
 370                 if (PS(use_cookies)) {
 371                         PS(send_cookie) = 1;
 372                 }
 373                 php_session_reset_id(TSRMLS_C);
 374                 PS(session_status) = php_session_active;
 375         }
 376 
 377         sd = ps_sd_lookup(data, PS(id), 0);
 378         if (sd) {
 379                 *vallen = sd->datalen;
 380                 *val = emalloc(sd->datalen + 1);
 381                 memcpy(*val, sd->data, sd->datalen);
 382                 (*val)[sd->datalen] = '\0';
 383                 ret = SUCCESS;
 384         }
 385 
 386         mm_unlock(data->mm);
 387 
 388         return ret;
 389 }
 390 
 391 PS_WRITE_FUNC(mm)
 392 {
 393         PS_MM_DATA;
 394         ps_sd *sd;
 395 
 396         mm_lock(data->mm, MM_LOCK_RW);
 397 
 398         sd = ps_sd_lookup(data, key, 1);
 399         if (!sd) {
 400                 sd = ps_sd_new(data, key);
 401                 ps_mm_debug(("new entry for %s\n", key));
 402         }
 403 
 404         if (sd) {
 405                 if (vallen >= sd->alloclen) {
 406                         if (data->mm) {
 407                                 mm_free(data->mm, sd->data);
 408                         }
 409                         sd->alloclen = vallen + 1;
 410                         sd->data = mm_malloc(data->mm, sd->alloclen);
 411 
 412                         if (!sd->data) {
 413                                 ps_sd_destroy(data, sd);
 414                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot allocate new data segment");
 415                                 sd = NULL;
 416                         }
 417                 }
 418                 if (sd) {
 419                         sd->datalen = vallen;
 420                         memcpy(sd->data, val, vallen);
 421                         time(&sd->ctime);
 422                 }
 423         }
 424 
 425         mm_unlock(data->mm);
 426 
 427         return sd ? SUCCESS : FAILURE;
 428 }
 429 
 430 PS_DESTROY_FUNC(mm)
 431 {
 432         PS_MM_DATA;
 433         ps_sd *sd;
 434 
 435         mm_lock(data->mm, MM_LOCK_RW);
 436 
 437         sd = ps_sd_lookup(data, key, 0);
 438         if (sd) {
 439                 ps_sd_destroy(data, sd);
 440         }
 441 
 442         mm_unlock(data->mm);
 443 
 444         return SUCCESS;
 445 }
 446 
 447 PS_GC_FUNC(mm)
 448 {
 449         PS_MM_DATA;
 450         time_t limit;
 451         ps_sd **ohash, **ehash;
 452         ps_sd *sd, *next;
 453 
 454         *nrdels = 0;
 455         ps_mm_debug(("gc\n"));
 456 
 457         time(&limit);
 458 
 459         limit -= maxlifetime;
 460 
 461         mm_lock(data->mm, MM_LOCK_RW);
 462 
 463         ehash = data->hash + data->hash_max + 1;
 464         for (ohash = data->hash; ohash < ehash; ohash++) {
 465                 for (sd = *ohash; sd; sd = next) {
 466                         next = sd->next;
 467                         if (sd->ctime < limit) {
 468                                 ps_mm_debug(("purging %s\n", sd->key));
 469                                 ps_sd_destroy(data, sd);
 470                                 (*nrdels)++;
 471                         }
 472                 }
 473         }
 474 
 475         mm_unlock(data->mm);
 476 
 477         return SUCCESS;
 478 }
 479 
 480 PS_CREATE_SID_FUNC(mm)
 481 {
 482         char *sid;
 483         int maxfail = 3;
 484         PS_MM_DATA;
 485 
 486         do {
 487                 sid = php_session_create_id((void **)&data, newlen TSRMLS_CC);
 488                 /* Check collision */
 489                 if (ps_mm_key_exists(data, sid TSRMLS_CC) == SUCCESS) {
 490                         if (sid) {
 491                                 efree(sid);
 492                                 sid = NULL;
 493                         }
 494                         if (!(maxfail--)) {
 495                                 return NULL;
 496                         }
 497                 }
 498         } while(!sid);
 499 
 500         return sid;
 501 }
 502 
 503 #endif
 504 
 505 /*
 506  * Local variables:
 507  * tab-width: 4
 508  * c-basic-offset: 4
 509  * End:
 510  * vim600: sw=4 ts=4 fdm=marker
 511  * vim<600: sw=4 ts=4
 512  */

/* [<][>][^][v][top][bottom][index][help] */