File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / session / mod_mm.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:03:55 2014 UTC (10 years ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29, HEAD
php 5.4.29

    1: /*
    2:    +----------------------------------------------------------------------+
    3:    | PHP Version 5                                                        |
    4:    +----------------------------------------------------------------------+
    5:    | Copyright (c) 1997-2014 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,v 1.1.1.5 2014/06/15 20:03:55 misho Exp $ */
   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: 		free(ps_mm_instance);
  275: 		ps_mm_instance = NULL;
  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>