You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

443 lines
9.0 KiB

20 years ago
25 years ago
24 years ago
25 years ago
20 years ago
25 years ago
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2007 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. /* $Id$ */
  19. #include "php.h"
  20. #ifdef HAVE_LIBMM
  21. #include <unistd.h>
  22. #include <mm.h>
  23. #include <time.h>
  24. #include <sys/stat.h>
  25. #include <sys/types.h>
  26. #include <fcntl.h>
  27. #include "php_session.h"
  28. #include "mod_mm.h"
  29. #include "SAPI.h"
  30. #ifdef ZTS
  31. # error mm is not thread-safe
  32. #endif
  33. #define PS_MM_FILE "session_mm_"
  34. /* For php_uint32 */
  35. #include "ext/standard/basic_functions.h"
  36. /*
  37. * this list holds all data associated with one session
  38. */
  39. typedef struct ps_sd {
  40. struct ps_sd *next;
  41. php_uint32 hv; /* hash value of key */
  42. time_t ctime; /* time of last change */
  43. void *data;
  44. size_t datalen; /* amount of valid data */
  45. size_t alloclen; /* amount of allocated memory for data */
  46. char key[1]; /* inline key */
  47. } ps_sd;
  48. typedef struct {
  49. MM *mm;
  50. ps_sd **hash;
  51. php_uint32 hash_max;
  52. php_uint32 hash_cnt;
  53. pid_t owner;
  54. } ps_mm;
  55. static ps_mm *ps_mm_instance = NULL;
  56. #if 0
  57. #define ps_mm_debug(a) printf a
  58. #else
  59. #define ps_mm_debug(a)
  60. #endif
  61. static inline php_uint32 ps_sd_hash(const char *data, int len)
  62. {
  63. php_uint32 h;
  64. const char *e = data + len;
  65. for (h = 2166136261U; data < e; ) {
  66. h *= 16777619;
  67. h ^= *data++;
  68. }
  69. return h;
  70. }
  71. static void hash_split(ps_mm *data)
  72. {
  73. php_uint32 nmax;
  74. ps_sd **nhash;
  75. ps_sd **ohash, **ehash;
  76. ps_sd *ps, *next;
  77. nmax = ((data->hash_max + 1) << 1) - 1;
  78. nhash = mm_calloc(data->mm, nmax + 1, sizeof(*data->hash));
  79. if (!nhash) {
  80. /* no further memory to expand hash table */
  81. return;
  82. }
  83. ehash = data->hash + data->hash_max + 1;
  84. for (ohash = data->hash; ohash < ehash; ohash++) {
  85. for (ps = *ohash; ps; ps = next) {
  86. next = ps->next;
  87. ps->next = nhash[ps->hv & nmax];
  88. nhash[ps->hv & nmax] = ps;
  89. }
  90. }
  91. mm_free(data->mm, data->hash);
  92. data->hash = nhash;
  93. data->hash_max = nmax;
  94. }
  95. static ps_sd *ps_sd_new(ps_mm *data, const char *key)
  96. {
  97. php_uint32 hv, slot;
  98. ps_sd *sd;
  99. int keylen;
  100. keylen = strlen(key);
  101. sd = mm_malloc(data->mm, sizeof(ps_sd) + keylen);
  102. if (!sd) {
  103. TSRMLS_FETCH();
  104. php_error_docref(NULL TSRMLS_CC, E_WARNING, "mm_malloc failed, avail %d, err %s", mm_available(data->mm), mm_error());
  105. return NULL;
  106. }
  107. hv = ps_sd_hash(key, keylen);
  108. slot = hv & data->hash_max;
  109. sd->ctime = 0;
  110. sd->hv = hv;
  111. sd->data = NULL;
  112. sd->alloclen = sd->datalen = 0;
  113. memcpy(sd->key, key, keylen + 1);
  114. sd->next = data->hash[slot];
  115. data->hash[slot] = sd;
  116. data->hash_cnt++;
  117. if (!sd->next) {
  118. if (data->hash_cnt >= data->hash_max)
  119. hash_split(data);
  120. }
  121. ps_mm_debug(("inserting %s(%p) into slot %d\n", key, sd, slot));
  122. return sd;
  123. }
  124. static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
  125. {
  126. php_uint32 slot;
  127. slot = ps_sd_hash(sd->key, strlen(sd->key)) & data->hash_max;
  128. if (data->hash[slot] == sd)
  129. data->hash[slot] = sd->next;
  130. else {
  131. ps_sd *prev;
  132. /* There must be some entry before the one we want to delete */
  133. for (prev = data->hash[slot]; prev->next != sd; prev = prev->next);
  134. prev->next = sd->next;
  135. }
  136. data->hash_cnt--;
  137. if (sd->data)
  138. mm_free(data->mm, sd->data);
  139. mm_free(data->mm, sd);
  140. }
  141. static ps_sd *ps_sd_lookup(ps_mm *data, const char *key, int rw)
  142. {
  143. php_uint32 hv, slot;
  144. ps_sd *ret, *prev;
  145. hv = ps_sd_hash(key, strlen(key));
  146. slot = hv & data->hash_max;
  147. for (prev = NULL, ret = data->hash[slot]; ret; prev = ret, ret = ret->next)
  148. if (ret->hv == hv && !strcmp(ret->key, key))
  149. break;
  150. if (ret && rw && ret != data->hash[slot]) {
  151. /* Move the entry to the top of the linked list */
  152. if (prev)
  153. prev->next = ret->next;
  154. ret->next = data->hash[slot];
  155. data->hash[slot] = ret;
  156. }
  157. ps_mm_debug(("lookup(%s): ret=%p,hv=%u,slot=%d\n", key, ret, hv, slot));
  158. return ret;
  159. }
  160. ps_module ps_mod_mm = {
  161. PS_MOD(mm)
  162. };
  163. #define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA()
  164. static int ps_mm_initialize(ps_mm *data, const char *path)
  165. {
  166. data->owner = getpid();
  167. data->mm = mm_create(0, path);
  168. if (!data->mm) {
  169. return FAILURE;
  170. }
  171. data->hash_cnt = 0;
  172. data->hash_max = 511;
  173. data->hash = mm_calloc(data->mm, data->hash_max + 1, sizeof(ps_sd *));
  174. if (!data->hash) {
  175. mm_destroy(data->mm);
  176. return FAILURE;
  177. }
  178. return SUCCESS;
  179. }
  180. static void ps_mm_destroy(ps_mm *data)
  181. {
  182. int h;
  183. ps_sd *sd, *next;
  184. /* This function is called during each module shutdown,
  185. but we must not release the shared memory pool, when
  186. an Apache child dies! */
  187. if (data->owner != getpid()) return;
  188. for (h = 0; h < data->hash_max + 1; h++)
  189. for (sd = data->hash[h]; sd; sd = next) {
  190. next = sd->next;
  191. ps_sd_destroy(data, sd);
  192. }
  193. mm_free(data->mm, data->hash);
  194. mm_destroy(data->mm);
  195. free(data);
  196. }
  197. PHP_MINIT_FUNCTION(ps_mm)
  198. {
  199. int save_path_len = strlen(PS(save_path));
  200. int mod_name_len = strlen(sapi_module.name);
  201. int euid_len;
  202. char *ps_mm_path, euid[30];
  203. int ret;
  204. ps_mm_instance = calloc(sizeof(*ps_mm_instance), 1);
  205. if (!ps_mm_instance) {
  206. return FAILURE;
  207. }
  208. if (!(euid_len = slprintf(euid, sizeof(euid), "%d", geteuid()))) {
  209. return FAILURE;
  210. }
  211. /* Directory + '/' + File + Module Name + Effective UID + \0 */
  212. ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1);
  213. memcpy(ps_mm_path, PS(save_path), save_path_len);
  214. if (PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) {
  215. ps_mm_path[save_path_len] = DEFAULT_SLASH;
  216. save_path_len++;
  217. }
  218. memcpy(ps_mm_path + save_path_len, PS_MM_FILE, sizeof(PS_MM_FILE) - 1);
  219. save_path_len += sizeof(PS_MM_FILE) - 1;
  220. memcpy(ps_mm_path + save_path_len, sapi_module.name, mod_name_len);
  221. save_path_len += mod_name_len;
  222. memcpy(ps_mm_path + save_path_len, euid, euid_len);
  223. ps_mm_path[save_path_len + euid_len] = '\0';
  224. ret = ps_mm_initialize(ps_mm_instance, ps_mm_path);
  225. efree(ps_mm_path);
  226. if (ret != SUCCESS) {
  227. free(ps_mm_instance);
  228. ps_mm_instance = NULL;
  229. return FAILURE;
  230. }
  231. php_session_register_module(&ps_mod_mm);
  232. return SUCCESS;
  233. }
  234. PHP_MSHUTDOWN_FUNCTION(ps_mm)
  235. {
  236. if (ps_mm_instance) {
  237. ps_mm_destroy(ps_mm_instance);
  238. return SUCCESS;
  239. }
  240. return FAILURE;
  241. }
  242. PS_OPEN_FUNC(mm)
  243. {
  244. ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance));
  245. if (!ps_mm_instance)
  246. return FAILURE;
  247. PS_SET_MOD_DATA(ps_mm_instance);
  248. return SUCCESS;
  249. }
  250. PS_CLOSE_FUNC(mm)
  251. {
  252. PS_SET_MOD_DATA(NULL);
  253. return SUCCESS;
  254. }
  255. PS_READ_FUNC(mm)
  256. {
  257. PS_MM_DATA;
  258. ps_sd *sd;
  259. int ret = FAILURE;
  260. mm_lock(data->mm, MM_LOCK_RD);
  261. sd = ps_sd_lookup(data, key, 0);
  262. if (sd) {
  263. *vallen = sd->datalen;
  264. *val = emalloc(sd->datalen + 1);
  265. memcpy(*val, sd->data, sd->datalen);
  266. (*val)[sd->datalen] = '\0';
  267. ret = SUCCESS;
  268. }
  269. mm_unlock(data->mm);
  270. return ret;
  271. }
  272. PS_WRITE_FUNC(mm)
  273. {
  274. PS_MM_DATA;
  275. ps_sd *sd;
  276. mm_lock(data->mm, MM_LOCK_RW);
  277. sd = ps_sd_lookup(data, key, 1);
  278. if (!sd) {
  279. sd = ps_sd_new(data, key);
  280. ps_mm_debug(("new entry for %s\n", key));
  281. }
  282. if (sd) {
  283. if (vallen >= sd->alloclen) {
  284. if (data->mm)
  285. mm_free(data->mm, sd->data);
  286. sd->alloclen = vallen + 1;
  287. sd->data = mm_malloc(data->mm, sd->alloclen);
  288. if (!sd->data) {
  289. ps_sd_destroy(data, sd);
  290. php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot allocate new data segment");
  291. sd = NULL;
  292. }
  293. }
  294. if (sd) {
  295. sd->datalen = vallen;
  296. memcpy(sd->data, val, vallen);
  297. time(&sd->ctime);
  298. }
  299. }
  300. mm_unlock(data->mm);
  301. return sd ? SUCCESS : FAILURE;
  302. }
  303. PS_DESTROY_FUNC(mm)
  304. {
  305. PS_MM_DATA;
  306. ps_sd *sd;
  307. mm_lock(data->mm, MM_LOCK_RW);
  308. sd = ps_sd_lookup(data, key, 0);
  309. if (sd)
  310. ps_sd_destroy(data, sd);
  311. mm_unlock(data->mm);
  312. return SUCCESS;
  313. }
  314. PS_GC_FUNC(mm)
  315. {
  316. PS_MM_DATA;
  317. time_t limit;
  318. ps_sd **ohash, **ehash;
  319. ps_sd *sd, *next;
  320. *nrdels = 0;
  321. ps_mm_debug(("gc\n"));
  322. time(&limit);
  323. limit -= maxlifetime;
  324. mm_lock(data->mm, MM_LOCK_RW);
  325. ehash = data->hash + data->hash_max + 1;
  326. for (ohash = data->hash; ohash < ehash; ohash++)
  327. for (sd = *ohash; sd; sd = next) {
  328. next = sd->next;
  329. if (sd->ctime < limit) {
  330. ps_mm_debug(("purging %s\n", sd->key));
  331. ps_sd_destroy(data, sd);
  332. (*nrdels)++;
  333. }
  334. }
  335. mm_unlock(data->mm);
  336. return SUCCESS;
  337. }
  338. #endif
  339. /*
  340. * Local variables:
  341. * tab-width: 4
  342. * c-basic-offset: 4
  343. * End:
  344. * vim600: sw=4 ts=4 fdm=marker
  345. * vim<600: sw=4 ts=4
  346. */