summaryrefslogtreecommitdiff
path: root/ext/session/mod_mm.c
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2013-03-14 05:42:27 +0000
committer <>2013-04-03 16:25:08 +0000
commitc4dd7a1a684490673e25aaf4fabec5df138854c4 (patch)
tree4d57c44caae4480efff02b90b9be86f44bf25409 /ext/session/mod_mm.c
downloadphp2-master.tar.gz
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'ext/session/mod_mm.c')
-rw-r--r--ext/session/mod_mm.c454
1 files changed, 454 insertions, 0 deletions
diff --git a/ext/session/mod_mm.c b/ext/session/mod_mm.c
new file mode 100644
index 0000000..e5406d0
--- /dev/null
+++ b/ext/session/mod_mm.c
@@ -0,0 +1,454 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 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
+ */