PageRenderTime 30ms CodeModel.GetById 3ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/session/mod_mm.c

http://github.com/php/php-src
C | 493 lines | 366 code | 101 blank | 26 comment | 60 complexity | 75990cc7e838f5fc300ac18f84b2c363 MD5 | raw file
Possible License(s): BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | http://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Author: Sascha Schumann <sascha@schumann.cx> |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "php.h"
  17. #ifdef HAVE_LIBMM
  18. #include <unistd.h>
  19. #include <mm.h>
  20. #include <time.h>
  21. #include <sys/stat.h>
  22. #include <sys/types.h>
  23. #include <fcntl.h>
  24. #include "php_stdint.h"
  25. #include "php_session.h"
  26. #include "mod_mm.h"
  27. #include "SAPI.h"
  28. #ifdef ZTS
  29. # error mm is not thread-safe
  30. #endif
  31. #define PS_MM_FILE "session_mm_"
  32. /* This list holds all data associated with one session. */
  33. typedef struct ps_sd {
  34. struct ps_sd *next;
  35. uint32_t hv; /* hash value of key */
  36. time_t ctime; /* time of last change */
  37. void *data;
  38. size_t datalen; /* amount of valid data */
  39. size_t alloclen; /* amount of allocated memory for data */
  40. char key[1]; /* inline key */
  41. } ps_sd;
  42. typedef struct {
  43. MM *mm;
  44. ps_sd **hash;
  45. uint32_t hash_max;
  46. uint32_t hash_cnt;
  47. pid_t owner;
  48. } ps_mm;
  49. static ps_mm *ps_mm_instance = NULL;
  50. #if 0
  51. # define ps_mm_debug(a) printf a
  52. #else
  53. # define ps_mm_debug(a)
  54. #endif
  55. static inline uint32_t ps_sd_hash(const char *data, int len)
  56. {
  57. uint32_t h;
  58. const char *e = data + len;
  59. for (h = 2166136261U; data < e; ) {
  60. h *= 16777619;
  61. h ^= *data++;
  62. }
  63. return h;
  64. }
  65. static void hash_split(ps_mm *data)
  66. {
  67. uint32_t nmax;
  68. ps_sd **nhash;
  69. ps_sd **ohash, **ehash;
  70. ps_sd *ps, *next;
  71. nmax = ((data->hash_max + 1) << 1) - 1;
  72. nhash = mm_calloc(data->mm, nmax + 1, sizeof(*data->hash));
  73. if (!nhash) {
  74. /* no further memory to expand hash table */
  75. return;
  76. }
  77. ehash = data->hash + data->hash_max + 1;
  78. for (ohash = data->hash; ohash < ehash; ohash++) {
  79. for (ps = *ohash; ps; ps = next) {
  80. next = ps->next;
  81. ps->next = nhash[ps->hv & nmax];
  82. nhash[ps->hv & nmax] = ps;
  83. }
  84. }
  85. mm_free(data->mm, data->hash);
  86. data->hash = nhash;
  87. data->hash_max = nmax;
  88. }
  89. static ps_sd *ps_sd_new(ps_mm *data, const char *key)
  90. {
  91. uint32_t hv, slot;
  92. ps_sd *sd;
  93. int keylen;
  94. keylen = strlen(key);
  95. sd = mm_malloc(data->mm, sizeof(ps_sd) + keylen);
  96. if (!sd) {
  97. php_error_docref(NULL, E_WARNING, "mm_malloc failed, avail %ld, err %s", mm_available(data->mm), mm_error());
  98. return NULL;
  99. }
  100. hv = ps_sd_hash(key, keylen);
  101. slot = hv & data->hash_max;
  102. sd->ctime = 0;
  103. sd->hv = hv;
  104. sd->data = NULL;
  105. sd->alloclen = sd->datalen = 0;
  106. memcpy(sd->key, key, keylen + 1);
  107. sd->next = data->hash[slot];
  108. data->hash[slot] = sd;
  109. data->hash_cnt++;
  110. if (!sd->next) {
  111. if (data->hash_cnt >= data->hash_max) {
  112. hash_split(data);
  113. }
  114. }
  115. ps_mm_debug(("inserting %s(%p) into slot %d\n", key, sd, slot));
  116. return sd;
  117. }
  118. static void ps_sd_destroy(ps_mm *data, ps_sd *sd)
  119. {
  120. uint32_t slot;
  121. slot = ps_sd_hash(sd->key, strlen(sd->key)) & data->hash_max;
  122. if (data->hash[slot] == sd) {
  123. data->hash[slot] = sd->next;
  124. } else {
  125. ps_sd *prev;
  126. /* There must be some entry before the one we want to delete */
  127. for (prev = data->hash[slot]; prev->next != sd; prev = prev->next);
  128. prev->next = sd->next;
  129. }
  130. data->hash_cnt--;
  131. if (sd->data) {
  132. mm_free(data->mm, sd->data);
  133. }
  134. mm_free(data->mm, sd);
  135. }
  136. static ps_sd *ps_sd_lookup(ps_mm *data, const char *key, int rw)
  137. {
  138. uint32_t hv, slot;
  139. ps_sd *ret, *prev;
  140. hv = ps_sd_hash(key, strlen(key));
  141. slot = hv & data->hash_max;
  142. for (prev = NULL, ret = data->hash[slot]; ret; prev = ret, ret = ret->next) {
  143. if (ret->hv == hv && !strcmp(ret->key, key)) {
  144. break;
  145. }
  146. }
  147. if (ret && rw && ret != data->hash[slot]) {
  148. /* Move the entry to the top of the linked list */
  149. if (prev) {
  150. prev->next = ret->next;
  151. }
  152. ret->next = data->hash[slot];
  153. data->hash[slot] = ret;
  154. }
  155. ps_mm_debug(("lookup(%s): ret=%p,hv=%u,slot=%d\n", key, ret, hv, slot));
  156. return ret;
  157. }
  158. static int ps_mm_key_exists(ps_mm *data, const char *key)
  159. {
  160. ps_sd *sd;
  161. if (!key) {
  162. return FAILURE;
  163. }
  164. sd = ps_sd_lookup(data, key, 0);
  165. if (sd) {
  166. return SUCCESS;
  167. }
  168. return FAILURE;
  169. }
  170. const ps_module ps_mod_mm = {
  171. PS_MOD_SID(mm)
  172. };
  173. #define PS_MM_DATA ps_mm *data = PS_GET_MOD_DATA()
  174. static int ps_mm_initialize(ps_mm *data, const char *path)
  175. {
  176. data->owner = getpid();
  177. data->mm = mm_create(0, path);
  178. if (!data->mm) {
  179. return FAILURE;
  180. }
  181. data->hash_cnt = 0;
  182. data->hash_max = 511;
  183. data->hash = mm_calloc(data->mm, data->hash_max + 1, sizeof(ps_sd *));
  184. if (!data->hash) {
  185. mm_destroy(data->mm);
  186. return FAILURE;
  187. }
  188. return SUCCESS;
  189. }
  190. static void ps_mm_destroy(ps_mm *data)
  191. {
  192. int h;
  193. ps_sd *sd, *next;
  194. /* This function is called during each module shutdown,
  195. but we must not release the shared memory pool, when
  196. an Apache child dies! */
  197. if (data->owner != getpid()) {
  198. return;
  199. }
  200. for (h = 0; h < data->hash_max + 1; h++) {
  201. for (sd = data->hash[h]; sd; sd = next) {
  202. next = sd->next;
  203. ps_sd_destroy(data, sd);
  204. }
  205. }
  206. mm_free(data->mm, data->hash);
  207. mm_destroy(data->mm);
  208. free(data);
  209. }
  210. PHP_MINIT_FUNCTION(ps_mm)
  211. {
  212. int save_path_len = strlen(PS(save_path));
  213. int mod_name_len = strlen(sapi_module.name);
  214. int euid_len;
  215. char *ps_mm_path, euid[30];
  216. int ret;
  217. ps_mm_instance = calloc(sizeof(*ps_mm_instance), 1);
  218. if (!ps_mm_instance) {
  219. return FAILURE;
  220. }
  221. if (!(euid_len = slprintf(euid, sizeof(euid), "%d", geteuid()))) {
  222. free(ps_mm_instance);
  223. ps_mm_instance = NULL;
  224. return FAILURE;
  225. }
  226. /* Directory + '/' + File + Module Name + Effective UID + \0 */
  227. ps_mm_path = emalloc(save_path_len + 1 + (sizeof(PS_MM_FILE) - 1) + mod_name_len + euid_len + 1);
  228. memcpy(ps_mm_path, PS(save_path), save_path_len);
  229. if (save_path_len && PS(save_path)[save_path_len - 1] != DEFAULT_SLASH) {
  230. ps_mm_path[save_path_len] = DEFAULT_SLASH;
  231. save_path_len++;
  232. }
  233. memcpy(ps_mm_path + save_path_len, PS_MM_FILE, sizeof(PS_MM_FILE) - 1);
  234. save_path_len += sizeof(PS_MM_FILE) - 1;
  235. memcpy(ps_mm_path + save_path_len, sapi_module.name, mod_name_len);
  236. save_path_len += mod_name_len;
  237. memcpy(ps_mm_path + save_path_len, euid, euid_len);
  238. ps_mm_path[save_path_len + euid_len] = '\0';
  239. ret = ps_mm_initialize(ps_mm_instance, ps_mm_path);
  240. efree(ps_mm_path);
  241. if (ret != SUCCESS) {
  242. free(ps_mm_instance);
  243. ps_mm_instance = NULL;
  244. return FAILURE;
  245. }
  246. php_session_register_module(&ps_mod_mm);
  247. return SUCCESS;
  248. }
  249. PHP_MSHUTDOWN_FUNCTION(ps_mm)
  250. {
  251. if (ps_mm_instance) {
  252. ps_mm_destroy(ps_mm_instance);
  253. return SUCCESS;
  254. }
  255. return FAILURE;
  256. }
  257. PS_OPEN_FUNC(mm)
  258. {
  259. ps_mm_debug(("open: ps_mm_instance=%p\n", ps_mm_instance));
  260. if (!ps_mm_instance) {
  261. return FAILURE;
  262. }
  263. PS_SET_MOD_DATA(ps_mm_instance);
  264. return SUCCESS;
  265. }
  266. PS_CLOSE_FUNC(mm)
  267. {
  268. PS_SET_MOD_DATA(NULL);
  269. return SUCCESS;
  270. }
  271. PS_READ_FUNC(mm)
  272. {
  273. PS_MM_DATA;
  274. ps_sd *sd;
  275. int ret = FAILURE;
  276. mm_lock(data->mm, MM_LOCK_RD);
  277. /* If there is an ID and strict mode, verify existence */
  278. if (PS(use_strict_mode)
  279. && ps_mm_key_exists(data, key->val) == FAILURE) {
  280. /* key points to PS(id), but cannot change here. */
  281. if (key) {
  282. efree(PS(id));
  283. PS(id) = NULL;
  284. }
  285. PS(id) = PS(mod)->s_create_sid((void **)&data);
  286. if (!PS(id)) {
  287. return FAILURE;
  288. }
  289. if (PS(use_cookies)) {
  290. PS(send_cookie) = 1;
  291. }
  292. php_session_reset_id();
  293. PS(session_status) = php_session_active;
  294. }
  295. sd = ps_sd_lookup(data, PS(id)->val, 0);
  296. if (sd) {
  297. *val = zend_string_init(sd->data, sd->datalen, 0);
  298. ret = SUCCESS;
  299. }
  300. mm_unlock(data->mm);
  301. return ret;
  302. }
  303. PS_WRITE_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->val, 1);
  309. if (!sd) {
  310. sd = ps_sd_new(data, key->val);
  311. ps_mm_debug(("new entry for %s\n", key->val));
  312. }
  313. if (sd) {
  314. if (val->len >= sd->alloclen) {
  315. if (data->mm) {
  316. mm_free(data->mm, sd->data);
  317. }
  318. sd->alloclen = val->len + 1;
  319. sd->data = mm_malloc(data->mm, sd->alloclen);
  320. if (!sd->data) {
  321. ps_sd_destroy(data, sd);
  322. php_error_docref(NULL, E_WARNING, "Cannot allocate new data segment");
  323. sd = NULL;
  324. }
  325. }
  326. if (sd) {
  327. sd->datalen = val->len;
  328. memcpy(sd->data, val->val, val->len);
  329. time(&sd->ctime);
  330. }
  331. }
  332. mm_unlock(data->mm);
  333. return sd ? SUCCESS : FAILURE;
  334. }
  335. PS_DESTROY_FUNC(mm)
  336. {
  337. PS_MM_DATA;
  338. ps_sd *sd;
  339. mm_lock(data->mm, MM_LOCK_RW);
  340. sd = ps_sd_lookup(data, key->val, 0);
  341. if (sd) {
  342. ps_sd_destroy(data, sd);
  343. }
  344. mm_unlock(data->mm);
  345. return SUCCESS;
  346. }
  347. PS_GC_FUNC(mm)
  348. {
  349. PS_MM_DATA;
  350. time_t limit;
  351. ps_sd **ohash, **ehash;
  352. ps_sd *sd, *next;
  353. *nrdels = 0;
  354. ps_mm_debug(("gc\n"));
  355. time(&limit);
  356. limit -= maxlifetime;
  357. mm_lock(data->mm, MM_LOCK_RW);
  358. ehash = data->hash + data->hash_max + 1;
  359. for (ohash = data->hash; ohash < ehash; ohash++) {
  360. for (sd = *ohash; sd; sd = next) {
  361. next = sd->next;
  362. if (sd->ctime < limit) {
  363. ps_mm_debug(("purging %s\n", sd->key));
  364. ps_sd_destroy(data, sd);
  365. (*nrdels)++;
  366. }
  367. }
  368. }
  369. mm_unlock(data->mm);
  370. return nrdels;
  371. }
  372. PS_CREATE_SID_FUNC(mm)
  373. {
  374. zend_string *sid;
  375. int maxfail = 3;
  376. PS_MM_DATA;
  377. do {
  378. sid = php_session_create_id((void **)&data);
  379. /* Check collision */
  380. if (ps_mm_key_exists(data, sid->val) == SUCCESS) {
  381. if (sid) {
  382. zend_string_release_ex(sid, 0);
  383. sid = NULL;
  384. }
  385. if (!(maxfail--)) {
  386. return NULL;
  387. }
  388. }
  389. } while(!sid);
  390. return sid;
  391. }
  392. #endif