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 7                                                        |
   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 
 126                 php_error_docref(NULL, E_WARNING, "mm_malloc failed, avail %ld, err %s", mm_available(data->mm), mm_error());
 127                 return NULL;
 128         }
 129 
 130         hv = ps_sd_hash(key, keylen);
 131         slot = hv & data->hash_max;
 132 
 133         sd->ctime = 0;
 134         sd->hv = hv;
 135         sd->data = NULL;
 136         sd->alloclen = sd->datalen = 0;
 137 
 138         memcpy(sd->key, key, keylen + 1);
 139 
 140         sd->next = data->hash[slot];
 141         data->hash[slot] = sd;
 142 
 143         data->hash_cnt++;
 144 
 145         if (!sd->next) {
 146                 if (data->hash_cnt >= data->hash_max) {
 147                         hash_split(data);
 148                 }
 149         }
 150 
 151         ps_mm_debug(("inserting %s(%p) into slot %d\n", key, sd, slot));
 152 
 153         return sd;
 154 }
 155 
 156 static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
 157 {
 158         php_uint32 slot;
 159 
 160         slot = ps_sd_hash(sd->key, strlen(sd->key)) & data->hash_max;
 161 
 162         if (data->hash[slot] == sd) {
 163                 data->hash[slot] = sd->next;
 164         } else {
 165                 ps_sd *prev;
 166 
 167                 /* There must be some entry before the one we want to delete */
 168                 for (prev = data->hash[slot]; prev->next != sd; prev = prev->next);
 169                 prev->next = sd->next;
 170         }
 171 
 172         data->hash_cnt--;
 173 
 174         if (sd->data) {
 175                 mm_free(data->mm, sd->data);
 176         }
 177 
 178         mm_free(data->mm, sd);
 179 }
 180 
 181 static ps_sd *ps_sd_lookup(ps_mm *data, const char *key, int rw)
 182 {
 183         php_uint32 hv, slot;
 184         ps_sd *ret, *prev;
 185 
 186         hv = ps_sd_hash(key, strlen(key));
 187         slot = hv & data->hash_max;
 188 
 189         for (prev = NULL, ret = data->hash[slot]; ret; prev = ret, ret = ret->next) {
 190                 if (ret->hv == hv && !strcmp(ret->key, key)) {
 191                         break;
 192                 }
 193         }
 194 
 195         if (ret && rw && ret != data->hash[slot]) {
 196                 /* Move the entry to the top of the linked list */
 197                 if (prev) {
 198                         prev->next = ret->next;
 199                 }
 200 
 201                 ret->next = data->hash[slot];
 202                 data->hash[slot] = ret;
 203         }
 204 
 205         ps_mm_debug(("lookup(%s): ret=%p,hv=%u,slot=%d\n", key, ret, hv, slot));
 206 
 207         return ret;
 208 }
 209 
 210 static int ps_mm_key_exists(ps_mm *data, const char *key)
 211 {
 212         ps_sd *sd;
 213 
 214         if (!key) {
 215                 return FAILURE;
 216         }
 217         sd = ps_sd_lookup(data, key, 0);
 218         if (sd) {
 219                 return SUCCESS;
 220         }
 221         return FAILURE;
 222 }
 223 
 224 ps_module ps_mod_mm = {
 225         PS_MOD_SID(mm)
 226 };
 227 
 228 #define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA()
 229 
 230 static int ps_mm_initialize(ps_mm *data, const char *path)
 231 {
 232         data->owner = getpid();
 233         data->mm = mm_create(0, path);
 234         if (!data->mm) {
 235                 return FAILURE;
 236         }
 237 
 238         data->hash_cnt = 0;
 239         data->hash_max = 511;
 240         data->hash = mm_calloc(data->mm, data->hash_max + 1, sizeof(ps_sd *));
 241         if (!data->hash) {
 242                 mm_destroy(data->mm);
 243                 return FAILURE;
 244         }
 245 
 246         return SUCCESS;
 247 }
 248 
 249 static void ps_mm_destroy(ps_mm *data)
 250 {
 251         int h;
 252         ps_sd *sd, *next;
 253 
 254         /* This function is called during each module shutdown,
 255            but we must not release the shared memory pool, when
 256            an Apache child dies! */
 257         if (data->owner != getpid()) {
 258                 return;
 259         }
 260 
 261         for (h = 0; h < data->hash_max + 1; h++) {
 262                 for (sd = data->hash[h]; sd; sd = next) {
 263                         next = sd->next;
 264                         ps_sd_destroy(data, sd);
 265                 }
 266         }
 267 
 268         mm_free(data->mm, data->hash);
 269         mm_destroy(data->mm);
 270         free(data);
 271 }
 272 
 273 PHP_MINIT_FUNCTION(ps_mm)
 274 {
 275         int save_path_len = strlen(PS(save_path));
 276         int mod_name_len = strlen(sapi_module.name);
 277         int euid_len;
 278         char *ps_mm_path, euid[30];
 279         int ret;
 280 
 281         ps_mm_instance = calloc(sizeof(*ps_mm_instance), 1);
 282         if (!ps_mm_instance) {
 283                 return FAILURE;
 284         }
 285 
 286         if (!(euid_len = slprintf(euid, sizeof(euid), "%d", geteuid()))) {
 287                 free(ps_mm_instance);
 288                 ps_mm_instance = NULL;
 289                 return FAILURE;
 290         }
 291 
 292         /* Directory + '/' + File + Module Name + Effective UID + \0 */
 293         ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1);
 294 
 295         memcpy(ps_mm_path, PS(save_path), save_path_len);
 296         if (save_path_len && PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) {
 297                 ps_mm_path[save_path_len] = DEFAULT_SLASH;
 298                 save_path_len++;
 299         }
 300         memcpy(ps_mm_path + save_path_len, PS_MM_FILE, sizeof(PS_MM_FILE) - 1);
 301         save_path_len += sizeof(PS_MM_FILE) - 1;
 302         memcpy(ps_mm_path + save_path_len, sapi_module.name, mod_name_len);
 303         save_path_len += mod_name_len;
 304         memcpy(ps_mm_path + save_path_len, euid, euid_len);
 305         ps_mm_path[save_path_len + euid_len] = '\0';
 306 
 307         ret = ps_mm_initialize(ps_mm_instance, ps_mm_path);
 308 
 309         efree(ps_mm_path);
 310 
 311         if (ret != SUCCESS) {
 312                 free(ps_mm_instance);
 313                 ps_mm_instance = NULL;
 314                 return FAILURE;
 315         }
 316 
 317         php_session_register_module(&ps_mod_mm);
 318         return SUCCESS;
 319 }
 320 
 321 PHP_MSHUTDOWN_FUNCTION(ps_mm)
 322 {
 323         if (ps_mm_instance) {
 324                 ps_mm_destroy(ps_mm_instance);
 325                 return SUCCESS;
 326         }
 327         return FAILURE;
 328 }
 329 
 330 PS_OPEN_FUNC(mm)
 331 {
 332         ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance));
 333 
 334         if (!ps_mm_instance) {
 335                 return FAILURE;
 336         }
 337         PS_SET_MOD_DATA(ps_mm_instance);
 338 
 339         return SUCCESS;
 340 }
 341 
 342 PS_CLOSE_FUNC(mm)
 343 {
 344         PS_SET_MOD_DATA(NULL);
 345 
 346         return SUCCESS;
 347 }
 348 
 349 PS_READ_FUNC(mm)
 350 {
 351         PS_MM_DATA;
 352         ps_sd *sd;
 353         int ret = FAILURE;
 354 
 355         mm_lock(data->mm, MM_LOCK_RD);
 356 
 357         /* If there is an ID and strict mode, verify existence */
 358         if (PS(use_strict_mode)
 359                 && ps_mm_key_exists(data, key->val) == FAILURE) {
 360                 /* key points to PS(id), but cannot change here. */
 361                 if (key) {
 362                         efree(PS(id));
 363                         PS(id) = NULL;
 364                 }
 365                 PS(id) = PS(mod)->s_create_sid((void **)&data);
 366                 if (!PS(id)) {
 367                         return FAILURE;
 368                 }
 369                 if (PS(use_cookies)) {
 370                         PS(send_cookie) = 1;
 371                 }
 372                 php_session_reset_id();
 373                 PS(session_status) = php_session_active;
 374         }
 375 
 376         sd = ps_sd_lookup(data, PS(id)->val, 0);
 377         if (sd) {
 378                 *val = zend_string_init(sd->data, sd->datalen, 0);
 379                 ret = SUCCESS;
 380         }
 381 
 382         mm_unlock(data->mm);
 383 
 384         return ret;
 385 }
 386 
 387 PS_WRITE_FUNC(mm)
 388 {
 389         PS_MM_DATA;
 390         ps_sd *sd;
 391 
 392         mm_lock(data->mm, MM_LOCK_RW);
 393 
 394         sd = ps_sd_lookup(data, key->val, 1);
 395         if (!sd) {
 396                 sd = ps_sd_new(data, key->val);
 397                 ps_mm_debug(("new entry for %s\n", key->val));
 398         }
 399 
 400         if (sd) {
 401                 if (val->len >= sd->alloclen) {
 402                         if (data->mm) {
 403                                 mm_free(data->mm, sd->data);
 404                         }
 405                         sd->alloclen = val->len + 1;
 406                         sd->data = mm_malloc(data->mm, sd->alloclen);
 407 
 408                         if (!sd->data) {
 409                                 ps_sd_destroy(data, sd);
 410                                 php_error_docref(NULL, E_WARNING, "cannot allocate new data segment");
 411                                 sd = NULL;
 412                         }
 413                 }
 414                 if (sd) {
 415                         sd->datalen = val->len;
 416                         memcpy(sd->data, val->val, val->len);
 417                         time(&sd->ctime);
 418                 }
 419         }
 420 
 421         mm_unlock(data->mm);
 422 
 423         return sd ? SUCCESS : FAILURE;
 424 }
 425 
 426 PS_DESTROY_FUNC(mm)
 427 {
 428         PS_MM_DATA;
 429         ps_sd *sd;
 430 
 431         mm_lock(data->mm, MM_LOCK_RW);
 432 
 433         sd = ps_sd_lookup(data, key->val, 0);
 434         if (sd) {
 435                 ps_sd_destroy(data, sd);
 436         }
 437 
 438         mm_unlock(data->mm);
 439 
 440         return SUCCESS;
 441 }
 442 
 443 PS_GC_FUNC(mm)
 444 {
 445         PS_MM_DATA;
 446         time_t limit;
 447         ps_sd **ohash, **ehash;
 448         ps_sd *sd, *next;
 449 
 450         *nrdels = 0;
 451         ps_mm_debug(("gc\n"));
 452 
 453         time(&limit);
 454 
 455         limit -= maxlifetime;
 456 
 457         mm_lock(data->mm, MM_LOCK_RW);
 458 
 459         ehash = data->hash + data->hash_max + 1;
 460         for (ohash = data->hash; ohash < ehash; ohash++) {
 461                 for (sd = *ohash; sd; sd = next) {
 462                         next = sd->next;
 463                         if (sd->ctime < limit) {
 464                                 ps_mm_debug(("purging %s\n", sd->key));
 465                                 ps_sd_destroy(data, sd);
 466                                 (*nrdels)++;
 467                         }
 468                 }
 469         }
 470 
 471         mm_unlock(data->mm);
 472 
 473         return SUCCESS;
 474 }
 475 
 476 PS_CREATE_SID_FUNC(mm)
 477 {
 478         zend_string *sid;
 479         int maxfail = 3;
 480         PS_MM_DATA;
 481 
 482         do {
 483                 sid = php_session_create_id((void **)&data);
 484                 /* Check collision */
 485                 if (ps_mm_key_exists(data, sid->val) == SUCCESS) {
 486                         if (sid) {
 487                                 zend_string_release(sid);
 488                                 sid = NULL;
 489                         }
 490                         if (!(maxfail--)) {
 491                                 return NULL;
 492                         }
 493                 }
 494         } while(!sid);
 495 
 496         return sid;
 497 }
 498 
 499 #endif
 500 
 501 /*
 502  * Local variables:
 503  * tab-width: 4
 504  * c-basic-offset: 4
 505  * End:
 506  * vim600: sw=4 ts=4 fdm=marker
 507  * vim<600: sw=4 ts=4
 508  */

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