mirror of
https://github.com/php/php-src.git
synced 2024-12-03 23:05:57 +08:00
455 lines
9.0 KiB
C
455 lines
9.0 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP Version 5 |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 1997-2012 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Author: Sascha Schumann <sascha@schumann.cx> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
/* $Id$ */
|
|
|
|
#include "php.h"
|
|
|
|
#ifdef HAVE_LIBMM
|
|
|
|
#include <unistd.h>
|
|
#include <mm.h>
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "php_session.h"
|
|
#include "mod_mm.h"
|
|
#include "SAPI.h"
|
|
|
|
#ifdef ZTS
|
|
# error mm is not thread-safe
|
|
#endif
|
|
|
|
#define PS_MM_FILE "session_mm_"
|
|
|
|
/* For php_uint32 */
|
|
#include "ext/standard/basic_functions.h"
|
|
|
|
/* This list holds all data associated with one session. */
|
|
|
|
typedef struct ps_sd {
|
|
struct ps_sd *next;
|
|
php_uint32 hv; /* hash value of key */
|
|
time_t ctime; /* time of last change */
|
|
void *data;
|
|
size_t datalen; /* amount of valid data */
|
|
size_t alloclen; /* amount of allocated memory for data */
|
|
char key[1]; /* inline key */
|
|
} ps_sd;
|
|
|
|
typedef struct {
|
|
MM *mm;
|
|
ps_sd **hash;
|
|
php_uint32 hash_max;
|
|
php_uint32 hash_cnt;
|
|
pid_t owner;
|
|
} ps_mm;
|
|
|
|
static ps_mm *ps_mm_instance = NULL;
|
|
|
|
#if 0
|
|
# define ps_mm_debug(a) printf a
|
|
#else
|
|
# define ps_mm_debug(a)
|
|
#endif
|
|
|
|
static inline php_uint32 ps_sd_hash(const char *data, int len)
|
|
{
|
|
php_uint32 h;
|
|
const char *e = data + len;
|
|
|
|
for (h = 2166136261U; data < e; ) {
|
|
h *= 16777619;
|
|
h ^= *data++;
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
static void hash_split(ps_mm *data)
|
|
{
|
|
php_uint32 nmax;
|
|
ps_sd **nhash;
|
|
ps_sd **ohash, **ehash;
|
|
ps_sd *ps, *next;
|
|
|
|
nmax = ((data->hash_max + 1) << 1) - 1;
|
|
nhash = mm_calloc(data->mm, nmax + 1, sizeof(*data->hash));
|
|
|
|
if (!nhash) {
|
|
/* no further memory to expand hash table */
|
|
return;
|
|
}
|
|
|
|
ehash = data->hash + data->hash_max + 1;
|
|
for (ohash = data->hash; ohash < ehash; ohash++) {
|
|
for (ps = *ohash; ps; ps = next) {
|
|
next = ps->next;
|
|
ps->next = nhash[ps->hv & nmax];
|
|
nhash[ps->hv & nmax] = ps;
|
|
}
|
|
}
|
|
mm_free(data->mm, data->hash);
|
|
|
|
data->hash = nhash;
|
|
data->hash_max = nmax;
|
|
}
|
|
|
|
static ps_sd *ps_sd_new(ps_mm *data, const char *key)
|
|
{
|
|
php_uint32 hv, slot;
|
|
ps_sd *sd;
|
|
int keylen;
|
|
|
|
keylen = strlen(key);
|
|
|
|
sd = mm_malloc(data->mm, sizeof(ps_sd) + keylen);
|
|
if (!sd) {
|
|
TSRMLS_FETCH();
|
|
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "mm_malloc failed, avail %d, err %s", mm_available(data->mm), mm_error());
|
|
return NULL;
|
|
}
|
|
|
|
hv = ps_sd_hash(key, keylen);
|
|
slot = hv & data->hash_max;
|
|
|
|
sd->ctime = 0;
|
|
sd->hv = hv;
|
|
sd->data = NULL;
|
|
sd->alloclen = sd->datalen = 0;
|
|
|
|
memcpy(sd->key, key, keylen + 1);
|
|
|
|
sd->next = data->hash[slot];
|
|
data->hash[slot] = sd;
|
|
|
|
data->hash_cnt++;
|
|
|
|
if (!sd->next) {
|
|
if (data->hash_cnt >= data->hash_max) {
|
|
hash_split(data);
|
|
}
|
|
}
|
|
|
|
ps_mm_debug(("inserting %s(%p) into slot %d\n", key, sd, slot));
|
|
|
|
return sd;
|
|
}
|
|
|
|
static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
|
|
{
|
|
php_uint32 slot;
|
|
|
|
slot = ps_sd_hash(sd->key, strlen(sd->key)) & data->hash_max;
|
|
|
|
if (data->hash[slot] == sd) {
|
|
data->hash[slot] = sd->next;
|
|
} else {
|
|
ps_sd *prev;
|
|
|
|
/* There must be some entry before the one we want to delete */
|
|
for (prev = data->hash[slot]; prev->next != sd; prev = prev->next);
|
|
prev->next = sd->next;
|
|
}
|
|
|
|
data->hash_cnt--;
|
|
|
|
if (sd->data) {
|
|
mm_free(data->mm, sd->data);
|
|
}
|
|
|
|
mm_free(data->mm, sd);
|
|
}
|
|
|
|
static ps_sd *ps_sd_lookup(ps_mm *data, const char *key, int rw)
|
|
{
|
|
php_uint32 hv, slot;
|
|
ps_sd *ret, *prev;
|
|
|
|
hv = ps_sd_hash(key, strlen(key));
|
|
slot = hv & data->hash_max;
|
|
|
|
for (prev = NULL, ret = data->hash[slot]; ret; prev = ret, ret = ret->next) {
|
|
if (ret->hv == hv && !strcmp(ret->key, key)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret && rw && ret != data->hash[slot]) {
|
|
/* Move the entry to the top of the linked list */
|
|
if (prev) {
|
|
prev->next = ret->next;
|
|
}
|
|
|
|
ret->next = data->hash[slot];
|
|
data->hash[slot] = ret;
|
|
}
|
|
|
|
ps_mm_debug(("lookup(%s): ret=%p,hv=%u,slot=%d\n", key, ret, hv, slot));
|
|
|
|
return ret;
|
|
}
|
|
|
|
ps_module ps_mod_mm = {
|
|
PS_MOD(mm)
|
|
};
|
|
|
|
#define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA()
|
|
|
|
static int ps_mm_initialize(ps_mm *data, const char *path)
|
|
{
|
|
data->owner = getpid();
|
|
data->mm = mm_create(0, path);
|
|
if (!data->mm) {
|
|
return FAILURE;
|
|
}
|
|
|
|
data->hash_cnt = 0;
|
|
data->hash_max = 511;
|
|
data->hash = mm_calloc(data->mm, data->hash_max + 1, sizeof(ps_sd *));
|
|
if (!data->hash) {
|
|
mm_destroy(data->mm);
|
|
return FAILURE;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
static void ps_mm_destroy(ps_mm *data)
|
|
{
|
|
int h;
|
|
ps_sd *sd, *next;
|
|
|
|
/* This function is called during each module shutdown,
|
|
but we must not release the shared memory pool, when
|
|
an Apache child dies! */
|
|
if (data->owner != getpid()) {
|
|
return;
|
|
}
|
|
|
|
for (h = 0; h < data->hash_max + 1; h++) {
|
|
for (sd = data->hash[h]; sd; sd = next) {
|
|
next = sd->next;
|
|
ps_sd_destroy(data, sd);
|
|
}
|
|
}
|
|
|
|
mm_free(data->mm, data->hash);
|
|
mm_destroy(data->mm);
|
|
free(data);
|
|
}
|
|
|
|
PHP_MINIT_FUNCTION(ps_mm)
|
|
{
|
|
int save_path_len = strlen(PS(save_path));
|
|
int mod_name_len = strlen(sapi_module.name);
|
|
int euid_len;
|
|
char *ps_mm_path, euid[30];
|
|
int ret;
|
|
|
|
ps_mm_instance = calloc(sizeof(*ps_mm_instance), 1);
|
|
if (!ps_mm_instance) {
|
|
return FAILURE;
|
|
}
|
|
|
|
if (!(euid_len = slprintf(euid, sizeof(euid), "%d", geteuid()))) {
|
|
return FAILURE;
|
|
}
|
|
|
|
/* Directory + '/' + File + Module Name + Effective UID + \0 */
|
|
ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1);
|
|
|
|
memcpy(ps_mm_path, PS(save_path), save_path_len);
|
|
if (save_path_len && PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) {
|
|
ps_mm_path[save_path_len] = DEFAULT_SLASH;
|
|
save_path_len++;
|
|
}
|
|
memcpy(ps_mm_path + save_path_len, PS_MM_FILE, sizeof(PS_MM_FILE) - 1);
|
|
save_path_len += sizeof(PS_MM_FILE) - 1;
|
|
memcpy(ps_mm_path + save_path_len, sapi_module.name, mod_name_len);
|
|
save_path_len += mod_name_len;
|
|
memcpy(ps_mm_path + save_path_len, euid, euid_len);
|
|
ps_mm_path[save_path_len + euid_len] = '\0';
|
|
|
|
ret = ps_mm_initialize(ps_mm_instance, ps_mm_path);
|
|
|
|
efree(ps_mm_path);
|
|
|
|
if (ret != SUCCESS) {
|
|
free(ps_mm_instance);
|
|
ps_mm_instance = NULL;
|
|
return FAILURE;
|
|
}
|
|
|
|
php_session_register_module(&ps_mod_mm);
|
|
return SUCCESS;
|
|
}
|
|
|
|
PHP_MSHUTDOWN_FUNCTION(ps_mm)
|
|
{
|
|
if (ps_mm_instance) {
|
|
ps_mm_destroy(ps_mm_instance);
|
|
return SUCCESS;
|
|
}
|
|
return FAILURE;
|
|
}
|
|
|
|
PS_OPEN_FUNC(mm)
|
|
{
|
|
ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance));
|
|
|
|
if (!ps_mm_instance) {
|
|
return FAILURE;
|
|
}
|
|
PS_SET_MOD_DATA(ps_mm_instance);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
PS_CLOSE_FUNC(mm)
|
|
{
|
|
PS_SET_MOD_DATA(NULL);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
PS_READ_FUNC(mm)
|
|
{
|
|
PS_MM_DATA;
|
|
ps_sd *sd;
|
|
int ret = FAILURE;
|
|
|
|
mm_lock(data->mm, MM_LOCK_RD);
|
|
|
|
sd = ps_sd_lookup(data, key, 0);
|
|
if (sd) {
|
|
*vallen = sd->datalen;
|
|
*val = emalloc(sd->datalen + 1);
|
|
memcpy(*val, sd->data, sd->datalen);
|
|
(*val)[sd->datalen] = '\0';
|
|
ret = SUCCESS;
|
|
}
|
|
|
|
mm_unlock(data->mm);
|
|
|
|
return ret;
|
|
}
|
|
|
|
PS_WRITE_FUNC(mm)
|
|
{
|
|
PS_MM_DATA;
|
|
ps_sd *sd;
|
|
|
|
mm_lock(data->mm, MM_LOCK_RW);
|
|
|
|
sd = ps_sd_lookup(data, key, 1);
|
|
if (!sd) {
|
|
sd = ps_sd_new(data, key);
|
|
ps_mm_debug(("new entry for %s\n", key));
|
|
}
|
|
|
|
if (sd) {
|
|
if (vallen >= sd->alloclen) {
|
|
if (data->mm) {
|
|
mm_free(data->mm, sd->data);
|
|
}
|
|
sd->alloclen = vallen + 1;
|
|
sd->data = mm_malloc(data->mm, sd->alloclen);
|
|
|
|
if (!sd->data) {
|
|
ps_sd_destroy(data, sd);
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot allocate new data segment");
|
|
sd = NULL;
|
|
}
|
|
}
|
|
if (sd) {
|
|
sd->datalen = vallen;
|
|
memcpy(sd->data, val, vallen);
|
|
time(&sd->ctime);
|
|
}
|
|
}
|
|
|
|
mm_unlock(data->mm);
|
|
|
|
return sd ? SUCCESS : FAILURE;
|
|
}
|
|
|
|
PS_DESTROY_FUNC(mm)
|
|
{
|
|
PS_MM_DATA;
|
|
ps_sd *sd;
|
|
|
|
mm_lock(data->mm, MM_LOCK_RW);
|
|
|
|
sd = ps_sd_lookup(data, key, 0);
|
|
if (sd) {
|
|
ps_sd_destroy(data, sd);
|
|
}
|
|
|
|
mm_unlock(data->mm);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
PS_GC_FUNC(mm)
|
|
{
|
|
PS_MM_DATA;
|
|
time_t limit;
|
|
ps_sd **ohash, **ehash;
|
|
ps_sd *sd, *next;
|
|
|
|
*nrdels = 0;
|
|
ps_mm_debug(("gc\n"));
|
|
|
|
time(&limit);
|
|
|
|
limit -= maxlifetime;
|
|
|
|
mm_lock(data->mm, MM_LOCK_RW);
|
|
|
|
ehash = data->hash + data->hash_max + 1;
|
|
for (ohash = data->hash; ohash < ehash; ohash++) {
|
|
for (sd = *ohash; sd; sd = next) {
|
|
next = sd->next;
|
|
if (sd->ctime < limit) {
|
|
ps_mm_debug(("purging %s\n", sd->key));
|
|
ps_sd_destroy(data, sd);
|
|
(*nrdels)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
mm_unlock(data->mm);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* End:
|
|
* vim600: sw=4 ts=4 fdm=marker
|
|
* vim<600: sw=4 ts=4
|
|
*/
|