PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/session/mod_mm.c

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