XCache is a fast, stable PHP opcode cacher that has been proven and is now running on production servers under high load. https://xcache.lighttpd.net/
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.

2158 lines
53 KiB

  1. #undef DEBUG
  2. /* {{{ macros */
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <signal.h>
  7. #include "php.h"
  8. #include "ext/standard/info.h"
  9. #include "ext/standard/md5.h"
  10. #include "ext/standard/php_math.h"
  11. #include "zend_extensions.h"
  12. #include "SAPI.h"
  13. #include "xcache.h"
  14. #include "optimizer.h"
  15. #include "coverager.h"
  16. #include "disassembler.h"
  17. #include "align.h"
  18. #include "stack.h"
  19. #include "xcache_globals.h"
  20. #include "processor.h"
  21. #include "utils.h"
  22. #include "const_string.h"
  23. #include "opcode_spec.h"
  24. #ifdef DEBUG
  25. # undef NDEBUG
  26. # undef inline
  27. # define inline
  28. #else
  29. # ifndef NDEBUG
  30. # define NDEBUG
  31. # endif
  32. #endif
  33. #include <assert.h>
  34. #define CHECK(x, e) do { if ((x) == NULL) { zend_error(E_ERROR, "XCache: " e); goto err; } } while (0)
  35. #define LOCK(x) xc_lock(x->lck)
  36. #define UNLOCK(x) xc_unlock(x->lck)
  37. #define ENTER_LOCK(x) do { \
  38. int catched = 0; \
  39. xc_lock(x->lck); \
  40. zend_try { \
  41. do
  42. #define LEAVE_LOCK(x) \
  43. while (0); \
  44. } zend_catch { \
  45. catched = 1; \
  46. } zend_end_try(); \
  47. xc_unlock(x->lck); \
  48. } while(0)
  49. /* }}} */
  50. /* {{{ globals */
  51. static char *xc_mmap_path = NULL;
  52. static char *xc_coredump_dir = NULL;
  53. static xc_hash_t xc_php_hcache = {0};
  54. static xc_hash_t xc_php_hentry = {0};
  55. static xc_hash_t xc_var_hcache = {0};
  56. static xc_hash_t xc_var_hentry = {0};
  57. /* total size */
  58. static zend_ulong xc_php_size = 0;
  59. static zend_ulong xc_var_size = 0;
  60. static xc_cache_t **xc_php_caches = NULL;
  61. static xc_cache_t **xc_var_caches = NULL;
  62. static zend_bool xc_initized = 0;
  63. static zend_compile_file_t *origin_compile_file;
  64. static zend_bool xc_test = 0;
  65. static zend_bool xc_readonly_protection = 0;
  66. static zend_bool xc_module_gotup = 0;
  67. static zend_bool xc_zend_extension_gotup = 0;
  68. #if !COMPILE_DL_XCACHE
  69. # define zend_extension_entry xcache_zend_extension_entry
  70. #endif
  71. ZEND_DLEXPORT zend_extension zend_extension_entry;
  72. ZEND_DECLARE_MODULE_GLOBALS(xcache);
  73. /* }}} */
  74. /* any function in *_dmz is only safe be called within locked(single thread) area */
  75. static inline int xc_entry_equal_dmz(xc_entry_t *a, xc_entry_t *b) /* {{{ */
  76. {
  77. /* this function isn't required but can be in dmz */
  78. if (a->type != b->type) {
  79. return 0;
  80. }
  81. switch (a->type) {
  82. case XC_TYPE_PHP:
  83. #ifdef HAVE_INODE
  84. do {
  85. xc_entry_data_php_t *ap = a->data.php;
  86. xc_entry_data_php_t *bp = b->data.php;
  87. return ap->inode == bp->inode
  88. && ap->device == bp->device;
  89. } while(0);
  90. #endif
  91. /* fall */
  92. case XC_TYPE_VAR:
  93. do {
  94. #ifdef IS_UNICODE
  95. if (a->name_type == IS_UNICODE) {
  96. if (a->name.ustr.len != b->name.ustr.len) {
  97. return 0;
  98. }
  99. return memcmp(a->name.ustr.val, b->name.ustr.val, (a->name.ustr.len + 1) * sizeof(UChar)) == 0;
  100. }
  101. else {
  102. return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
  103. }
  104. #else
  105. return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
  106. #endif
  107. } while(0);
  108. default:
  109. assert(0);
  110. }
  111. return 0;
  112. }
  113. /* }}} */
  114. static void xc_entry_free_dmz(volatile xc_entry_t *xce) /* {{{ */
  115. {
  116. xc_mem_free(xce->cache->mem, (xc_entry_t *)xce);
  117. }
  118. /* }}} */
  119. static void xc_entry_add_dmz(xc_entry_t *xce) /* {{{ */
  120. {
  121. xc_entry_t **head = &(xce->cache->entries[xce->hvalue]);
  122. xce->next = *head;
  123. *head = xce;
  124. xce->cache->entries_count ++;
  125. }
  126. /* }}} */
  127. static xc_entry_t *xc_entry_store_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
  128. {
  129. xc_entry_t *stored_xce;
  130. xce->hits = 0;
  131. xce->ctime = XG(request_time);
  132. xce->atime = XG(request_time);
  133. stored_xce = xc_processor_store_xc_entry_t(xce TSRMLS_CC);
  134. if (stored_xce) {
  135. xc_entry_add_dmz(stored_xce);
  136. return stored_xce;
  137. }
  138. else {
  139. xce->cache->ooms ++;
  140. return NULL;
  141. }
  142. }
  143. /* }}} */
  144. static void xc_entry_remove_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
  145. {
  146. xc_entry_t **last = &(xce->cache->entries[xce->hvalue]);
  147. xc_entry_t *p;
  148. for (p = *last; p; last = &(p->next), p = p->next) {
  149. if (xc_entry_equal_dmz(xce, p)) {
  150. *last = p->next;
  151. xce->cache->entries_count ++;
  152. if (p->refcount == 0) {
  153. xc_entry_free_dmz(p);
  154. }
  155. else {
  156. p->next = p->cache->deletes;
  157. p->cache->deletes = p;
  158. p->dtime = XG(request_time);
  159. xce->cache->deletes_count ++;
  160. }
  161. return;
  162. }
  163. }
  164. assert(0);
  165. }
  166. /* }}} */
  167. static xc_entry_t *xc_entry_find_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
  168. {
  169. xc_entry_t *p;
  170. for (p = xce->cache->entries[xce->hvalue]; p; p = p->next) {
  171. if (xc_entry_equal_dmz(xce, p)) {
  172. if (p->type == XC_TYPE_VAR || /* PHP */ p->data.php->mtime == xce->data.php->mtime) {
  173. p->hits ++;
  174. p->atime = XG(request_time);
  175. return p;
  176. }
  177. else {
  178. xc_entry_remove_dmz(p TSRMLS_CC);
  179. return NULL;
  180. }
  181. }
  182. }
  183. return NULL;
  184. }
  185. /* }}} */
  186. static void xc_entry_hold_php_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
  187. {
  188. xce->refcount ++;
  189. xc_stack_push(&XG(php_holds)[xce->cache->cacheid], (void *)xce);
  190. }
  191. /* }}} */
  192. #if 0
  193. static void xc_entry_hold_var_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
  194. {
  195. xce->refcount ++;
  196. xc_stack_push(&XG(var_holds)[xce->cache->cacheid], (void *)xce);
  197. }
  198. /* }}} */
  199. #endif
  200. /* helper functions for user functions */
  201. static void xc_fillinfo_dmz(xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
  202. {
  203. zval *blocks;
  204. const xc_block_t *b;
  205. #ifndef NDEBUG
  206. xc_memsize_t avail = 0;
  207. #endif
  208. xc_mem_t *mem = cache->mem;
  209. add_assoc_long_ex(return_value, ZEND_STRS("slots"), cache->hentry->size);
  210. add_assoc_long_ex(return_value, ZEND_STRS("compiling"), cache->compiling);
  211. add_assoc_long_ex(return_value, ZEND_STRS("misses"), cache->misses);
  212. add_assoc_long_ex(return_value, ZEND_STRS("hits"), cache->hits);
  213. add_assoc_long_ex(return_value, ZEND_STRS("clogs"), cache->clogs);
  214. add_assoc_long_ex(return_value, ZEND_STRS("ooms"), cache->ooms);
  215. add_assoc_long_ex(return_value, ZEND_STRS("cached"), cache->entries_count);
  216. add_assoc_long_ex(return_value, ZEND_STRS("deleted"), cache->deletes_count);
  217. MAKE_STD_ZVAL(blocks);
  218. array_init(blocks);
  219. add_assoc_long_ex(return_value, ZEND_STRS("size"), xc_mem_size(mem));
  220. add_assoc_long_ex(return_value, ZEND_STRS("avail"), xc_mem_avail(mem));
  221. add_assoc_bool_ex(return_value, ZEND_STRS("can_readonly"), xc_readonly_protection);
  222. for (b = xc_mem_freeblock_first(mem); b; b = xc_mem_freeblock_next(b)) {
  223. zval *bi;
  224. MAKE_STD_ZVAL(bi);
  225. array_init(bi);
  226. add_assoc_long_ex(bi, ZEND_STRS("size"), xc_mem_block_size(b));
  227. add_assoc_long_ex(bi, ZEND_STRS("offset"), xc_mem_block_offset(mem, b));
  228. add_next_index_zval(blocks, bi);
  229. #ifndef NDEBUG
  230. avail += b->size;
  231. #endif
  232. }
  233. add_assoc_zval_ex(return_value, ZEND_STRS("free_blocks"), blocks);
  234. assert(avail == xc_mem_avail(mem));
  235. }
  236. /* }}} */
  237. static void xc_fillentry_dmz(xc_entry_t *entry, int del, zval *list TSRMLS_DC) /* {{{ */
  238. {
  239. zval* ei;
  240. xc_entry_data_php_t *php;
  241. xc_entry_data_var_t *var;
  242. ALLOC_INIT_ZVAL(ei);
  243. array_init(ei);
  244. add_assoc_long_ex(ei, ZEND_STRS("size"), entry->size);
  245. add_assoc_long_ex(ei, ZEND_STRS("refcount"), entry->refcount);
  246. add_assoc_long_ex(ei, ZEND_STRS("hits"), entry->hits);
  247. add_assoc_long_ex(ei, ZEND_STRS("ctime"), entry->ctime);
  248. add_assoc_long_ex(ei, ZEND_STRS("atime"), entry->atime);
  249. add_assoc_long_ex(ei, ZEND_STRS("dtime"), entry->dtime);
  250. #ifdef IS_UNICODE
  251. do {
  252. zval *zv;
  253. ALLOC_INIT_ZVAL(zv);
  254. switch (entry->name_type) {
  255. case IS_UNICODE:
  256. ZVAL_UNICODEL(zv, entry->name.ustr.val, entry->name.ustr.len, 1);
  257. break;
  258. case IS_STRING:
  259. ZVAL_STRINGL(zv, entry->name.str.val, entry->name.str.len, 1);
  260. break;
  261. default:
  262. assert(0);
  263. }
  264. zv->type = entry->name_type;
  265. add_assoc_zval_ex(ei, ZEND_STRS("name"), zv);
  266. } while (0);
  267. #else
  268. add_assoc_stringl_ex(ei, ZEND_STRS("name"), entry->name.str.val, entry->name.str.len + 1, 1);
  269. #endif
  270. switch (entry->type) {
  271. case XC_TYPE_PHP:
  272. php = entry->data.php;
  273. add_assoc_long_ex(ei, ZEND_STRS("sourcesize"), php->sourcesize);
  274. #ifdef HAVE_INODE
  275. add_assoc_long_ex(ei, ZEND_STRS("device"), php->device);
  276. add_assoc_long_ex(ei, ZEND_STRS("inode"), php->inode);
  277. #endif
  278. add_assoc_long_ex(ei, ZEND_STRS("mtime"), php->mtime);
  279. add_assoc_long_ex(ei, ZEND_STRS("function_cnt"), php->funcinfo_cnt);
  280. add_assoc_long_ex(ei, ZEND_STRS("class_cnt"), php->classinfo_cnt);
  281. break;
  282. case XC_TYPE_VAR:
  283. var = entry->data.var;
  284. break;
  285. default:
  286. assert(0);
  287. }
  288. add_next_index_zval(list, ei);
  289. }
  290. /* }}} */
  291. static void xc_filllist_dmz(xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
  292. {
  293. zval* list;
  294. int i, c;
  295. xc_entry_t *e;
  296. ALLOC_INIT_ZVAL(list);
  297. array_init(list);
  298. for (i = 0, c = cache->hentry->size; i < c; i ++) {
  299. for (e = cache->entries[i]; e; e = e->next) {
  300. xc_fillentry_dmz(e, 0, list TSRMLS_CC);
  301. }
  302. }
  303. add_assoc_zval(return_value, "cache_list", list);
  304. ALLOC_INIT_ZVAL(list);
  305. array_init(list);
  306. for (e = cache->deletes; e; e = e->next) {
  307. xc_fillentry_dmz(e, 1, list TSRMLS_CC);
  308. }
  309. add_assoc_zval(return_value, "deleted_list", list);
  310. }
  311. /* }}} */
  312. static zend_op_array *xc_entry_install(xc_entry_t *xce, zend_file_handle *h TSRMLS_DC) /* {{{ */
  313. {
  314. zend_uint i;
  315. xc_entry_data_php_t *p = xce->data.php;
  316. #ifndef ZEND_ENGINE_2
  317. /* new ptr which is stored inside CG(class_table) */
  318. xc_cest_t **new_cest_ptrs = (xc_cest_t **)do_alloca(sizeof(xc_cest_t*) * p->classinfo_cnt);
  319. #endif
  320. /* install function */
  321. for (i = 0; i < p->funcinfo_cnt; i ++) {
  322. xc_funcinfo_t *fi = &p->funcinfos[i];
  323. xc_install_function(xce->name.str.val, &fi->func,
  324. UNISW(0, fi->type), fi->key, fi->key_size TSRMLS_CC);
  325. }
  326. /* install class */
  327. for (i = 0; i < p->classinfo_cnt; i ++) {
  328. xc_classinfo_t *ci = &p->classinfos[i];
  329. #ifndef ZEND_ENGINE_2
  330. zend_class_entry *ce = CestToCePtr(ci->cest);
  331. /* fix pointer to the be which inside class_table */
  332. if (ce->parent) {
  333. zend_uint class_idx = (/* class_num */ (int) ce->parent) - 1;
  334. assert(class_idx < i);
  335. ci->cest.parent = new_cest_ptrs[class_idx];
  336. }
  337. new_cest_ptrs[i] =
  338. #endif
  339. xc_install_class(xce->name.str.val, &ci->cest,
  340. UNISW(0, ci->type), ci->key, ci->key_size TSRMLS_CC);
  341. }
  342. i = 1;
  343. zend_hash_add(&EG(included_files), xce->name.str.val, xce->name.str.len+1, (void *)&i, sizeof(int), NULL);
  344. zend_llist_add_element(&CG(open_files), h);
  345. #ifndef ZEND_ENGINE_2
  346. free_alloca(new_cest_ptrs);
  347. #endif
  348. return p->op_array;
  349. }
  350. /* }}} */
  351. static void xc_entry_gc_real(xc_cache_t **caches, int size TSRMLS_DC) /* {{{ */
  352. {
  353. time_t t = XG(request_time);
  354. int i;
  355. xc_cache_t *cache;
  356. typedef xc_entry_t *xc_delete_t;
  357. xc_delete_t p, *last;
  358. for (i = 0; i < size; i ++) {
  359. cache = caches[i];
  360. ENTER_LOCK(cache) {
  361. if (cache->deletes) {
  362. last = (xc_delete_t *) &cache->deletes;
  363. for (p = *last; p; p = *last) {
  364. if (t - p->dtime > 3600) {
  365. p->refcount = 0;
  366. /* issue warning here */
  367. }
  368. if (p->refcount == 0) {
  369. *last = p->next;
  370. cache->deletes_count --;
  371. xc_entry_free_dmz(p);
  372. }
  373. else {
  374. last = &(p->next);
  375. }
  376. }
  377. }
  378. } LEAVE_LOCK(cache);
  379. }
  380. }
  381. /* }}} */
  382. static void xc_entry_gc(TSRMLS_D) /* {{{ */
  383. {
  384. xc_entry_gc_real(xc_php_caches, xc_php_hcache.size TSRMLS_CC);
  385. xc_entry_gc_real(xc_var_caches, xc_var_hcache.size TSRMLS_CC);
  386. }
  387. /* }}} */
  388. static inline void xc_entry_unholds_real(xc_stack_t *holds, xc_cache_t **caches, int cachecount TSRMLS_DC) /* {{{ */
  389. {
  390. int i;
  391. xc_stack_t *s;
  392. xc_cache_t *cache;
  393. xc_entry_t *xce;
  394. for (i = 0; i < cachecount; i ++) {
  395. s = &holds[i];
  396. if (xc_stack_size(s)) {
  397. cache = ((xc_entry_t *)xc_stack_top(s))->cache;
  398. ENTER_LOCK(cache) {
  399. while (xc_stack_size(holds)) {
  400. xce = (xc_entry_t*) xc_stack_pop(holds);
  401. xce->refcount --;
  402. assert(xce->refcount >= 0);
  403. }
  404. } LEAVE_LOCK(cache);
  405. }
  406. }
  407. }
  408. /* }}} */
  409. static void xc_entry_unholds(TSRMLS_D) /* {{{ */
  410. {
  411. xc_entry_unholds_real(XG(php_holds), xc_php_caches, xc_php_hcache.size TSRMLS_CC);
  412. xc_entry_unholds_real(XG(var_holds), xc_var_caches, xc_var_hcache.size TSRMLS_CC);
  413. }
  414. /* }}} */
  415. static int xc_stat(const char *filename, const char *include_path, struct stat *pbuf TSRMLS_DC) /* {{{ */
  416. {
  417. char filepath[1024];
  418. char *paths, *path;
  419. char *tokbuf;
  420. int size = strlen(include_path) + 1;
  421. char tokens[] = { DEFAULT_DIR_SEPARATOR, '\0' };
  422. paths = (char *)do_alloca(size);
  423. memcpy(paths, include_path, size);
  424. for (path = php_strtok_r(paths, tokens, &tokbuf); path; path = php_strtok_r(NULL, tokens, &tokbuf)) {
  425. if (strlen(path) + strlen(filename) + 1 > 1024) {
  426. continue;
  427. }
  428. snprintf(filepath, sizeof(filepath), "%s/%s", path, filename);
  429. if (VCWD_STAT(filepath, pbuf) == 0) {
  430. free_alloca(paths);
  431. return 0;
  432. }
  433. }
  434. free_alloca(paths);
  435. return 1;
  436. }
  437. /* }}} */
  438. #define HASH(i) (i)
  439. #define HASH_USTR_L(t, s, l) HASH(zend_u_inline_hash_func(t, s, (l + 1) * sizeof(UChar)))
  440. #define HASH_STR_L(s, l) HASH(zend_inline_hash_func(s, l + 1))
  441. #define HASH_STR(s) HASH_STR_L(s, strlen(s) + 1)
  442. #define HASH_NUM(n) HASH(n)
  443. static inline xc_hash_value_t xc_entry_hash_var(xc_entry_t *xce) /* {{{ */
  444. {
  445. return UNISW(NOTHING, UG(unicode) ? HASH_USTR_L(xce->name_type, (char *)xce->name.ustr.val, xce->name.ustr.len) :)
  446. HASH_STR_L(xce->name.str.val, xce->name.str.len);
  447. }
  448. /* }}} */
  449. static inline xc_hash_value_t xc_entry_hash_php(xc_entry_t *xce) /* {{{ */
  450. {
  451. #ifdef HAVE_INODE
  452. return HASH(xce->data.php->device + xce->data.php->inode);
  453. #else
  454. return xc_entry_hash_var(xce);
  455. #endif
  456. }
  457. /* }}} */
  458. static int xc_entry_init_key_php(xc_entry_t *xce, char *filename, char *opened_path_buffer TSRMLS_DC) /* {{{ */
  459. {
  460. struct stat buf, *pbuf;
  461. xc_hash_value_t hv;
  462. int cacheid;
  463. xc_entry_data_php_t *php;
  464. if (!filename || !SG(request_info).path_translated) {
  465. return 0;
  466. }
  467. do {
  468. if (strcmp(SG(request_info).path_translated, filename) == 0) {
  469. /* sapi has already done this stat() for us */
  470. pbuf = sapi_get_stat(TSRMLS_C);
  471. if (pbuf) {
  472. break;
  473. }
  474. }
  475. pbuf = &buf;
  476. if (IS_ABSOLUTE_PATH(filename, strlen(filename))) {
  477. if (VCWD_STAT(filename, pbuf) != 0) {
  478. return 0;
  479. }
  480. }
  481. else {
  482. if (xc_stat(filename, PG(include_path), pbuf TSRMLS_CC) != 0) {
  483. return 0;
  484. }
  485. }
  486. } while (0);
  487. if (XG(request_time) - pbuf->st_mtime < 2) {
  488. return 0;
  489. }
  490. #ifndef HAVE_INODE
  491. /* hash on filename, let's expand it to real path */
  492. filename = expand_filepath(filename, opened_path_buffer TSRMLS_CC);
  493. if (filename == NULL) {
  494. return 0;
  495. }
  496. #endif
  497. UNISW(NOTHING, xce->name_type = IS_STRING;)
  498. xce->name.str.val = filename;
  499. xce->name.str.len = strlen(filename);
  500. php = xce->data.php;
  501. php->mtime = pbuf->st_mtime;
  502. #ifdef HAVE_INODE
  503. php->device = pbuf->st_dev;
  504. php->inode = pbuf->st_ino;
  505. #endif
  506. php->sourcesize = pbuf->st_size;
  507. hv = xc_entry_hash_php(xce);
  508. cacheid = (hv & xc_php_hcache.mask);
  509. xce->cache = xc_php_caches[cacheid];
  510. hv >>= xc_php_hcache.bits;
  511. xce->hvalue = (hv & xc_php_hentry.mask);
  512. xce->type = XC_TYPE_PHP;
  513. return 1;
  514. }
  515. /* }}} */
  516. static zend_op_array *xc_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
  517. {
  518. xc_sandbox_t sandbox;
  519. zend_op_array *op_array;
  520. xc_entry_t xce, *stored_xce;
  521. xc_entry_data_php_t php;
  522. xc_cache_t *cache;
  523. zend_bool clogged = 0;
  524. zend_bool catched = 0;
  525. char *filename;
  526. char opened_path_buffer[MAXPATHLEN];
  527. if (!xc_initized) {
  528. assert(0);
  529. }
  530. if (!XG(cacher)) {
  531. op_array = origin_compile_file(h, type TSRMLS_CC);
  532. #ifdef HAVE_XCACHE_OPTIMIZER
  533. if (XG(optimizer)) {
  534. xc_optimize(op_array TSRMLS_CC);
  535. }
  536. #endif
  537. return op_array;
  538. }
  539. /* {{{ prepare key
  540. * include_once() and require_once() gives us opened_path
  541. * however, include() and require() non-absolute path which break
  542. * included_files, and may confuse with (include|require)_once
  543. * -- Xuefer
  544. */
  545. filename = h->opened_path ? h->opened_path : h->filename;
  546. xce.data.php = &php;
  547. if (!xc_entry_init_key_php(&xce, filename, opened_path_buffer TSRMLS_CC)) {
  548. return origin_compile_file(h, type TSRMLS_CC);
  549. }
  550. cache = xce.cache;
  551. /* }}} */
  552. /* {{{ restore */
  553. /* stale precheck */
  554. if (cache->compiling) {
  555. cache->clogs ++; /* is it safe here? */
  556. return origin_compile_file(h, type TSRMLS_CC);
  557. }
  558. stored_xce = NULL;
  559. op_array = NULL;
  560. ENTER_LOCK(cache) {
  561. /* clogged */
  562. if (cache->compiling) {
  563. cache->clogs ++;
  564. op_array = NULL;
  565. clogged = 1;
  566. break;
  567. }
  568. stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
  569. /* found */
  570. if (stored_xce) {
  571. #ifdef DEBUG
  572. fprintf(stderr, "found %s, catch it\n", stored_xce->name.str.val);
  573. #endif
  574. xc_entry_hold_php_dmz(stored_xce TSRMLS_CC);
  575. cache->hits ++;
  576. break;
  577. }
  578. cache->compiling = XG(request_time);
  579. cache->misses ++;
  580. } LEAVE_LOCK(cache);
  581. /* found */
  582. if (stored_xce) {
  583. goto restore;
  584. }
  585. /* clogged */
  586. if (clogged) {
  587. return origin_compile_file(h, type TSRMLS_CC);
  588. }
  589. /* }}} */
  590. /* {{{ compile */
  591. #ifdef DEBUG
  592. fprintf(stderr, "compiling %s\n", filename);
  593. #endif
  594. /* make compile inside sandbox */
  595. xc_sandbox_init(&sandbox, filename TSRMLS_CC);
  596. zend_try {
  597. op_array = origin_compile_file(h, type TSRMLS_CC);
  598. } zend_catch {
  599. catched = 1;
  600. } zend_end_try();
  601. if (catched) {
  602. goto err_bailout;
  603. }
  604. if (op_array == NULL) {
  605. goto err_oparray;
  606. }
  607. filename = h->opened_path ? h->opened_path : h->filename;
  608. #ifdef HAVE_INODE
  609. if (xce.name.str.val != filename) {
  610. xce.name.str.val = filename;
  611. xce.name.str.len = strlen(filename);
  612. }
  613. #endif
  614. #ifdef HAVE_XCACHE_OPTIMIZER
  615. if (XG(optimizer)) {
  616. xc_optimize(op_array TSRMLS_CC);
  617. }
  618. #endif
  619. php.op_array = op_array;
  620. php.funcinfo_cnt = zend_hash_num_elements(CG(function_table));
  621. php.classinfo_cnt = zend_hash_num_elements(CG(class_table));
  622. php.funcinfos = ECALLOC_N(php.funcinfos, php.funcinfo_cnt);
  623. if (!php.funcinfos) {
  624. goto err_func;
  625. }
  626. php.classinfos = ECALLOC_N(php.classinfos, php.classinfo_cnt);
  627. if (!php.classinfos) {
  628. goto err_class;
  629. }
  630. /* }}} */
  631. /* {{{ shallow copy, pointers only */ {
  632. Bucket *b;
  633. unsigned int i;
  634. b = CG(function_table)->pListHead;
  635. for (i = 0; b; i ++, b = b->pListNext) {
  636. xc_funcinfo_t *fi = &php.funcinfos[i];
  637. assert(i < php.funcinfo_cnt);
  638. assert(b->pData);
  639. memcpy(&fi->func, b->pData, sizeof(zend_function));
  640. UNISW(NOTHING, fi->type = b->key.type;)
  641. fi->key = BUCKET_KEY(b);
  642. fi->key_size = b->nKeyLength;
  643. }
  644. b = CG(class_table)->pListHead;
  645. for (i = 0; b; i ++, b = b->pListNext) {
  646. xc_classinfo_t *ci = &php.classinfos[i];
  647. assert(i < php.classinfo_cnt);
  648. assert(b->pData);
  649. memcpy(&ci->cest, b->pData, sizeof(xc_cest_t));
  650. UNISW(NOTHING, ci->type = b->key.type;)
  651. ci->key = BUCKET_KEY(b);
  652. ci->key_size = b->nKeyLength;
  653. /* need to fix inside store */
  654. }
  655. }
  656. /* }}} */
  657. xc_entry_gc(TSRMLS_C);
  658. ENTER_LOCK(cache) { /* {{{ store/add entry */
  659. stored_xce = xc_entry_store_dmz(&xce TSRMLS_CC);
  660. } LEAVE_LOCK(cache);
  661. /* }}} */
  662. #ifdef DEBUG
  663. fprintf(stderr, "stored\n");
  664. #endif
  665. efree(xce.data.php->classinfos);
  666. err_class:
  667. efree(xce.data.php->funcinfos);
  668. err_func:
  669. err_oparray:
  670. err_bailout:
  671. if (xc_test && stored_xce) {
  672. /* no install, keep open_files too for h */
  673. xc_sandbox_free(&sandbox, 0 TSRMLS_CC);
  674. sandbox.tmp_open_files->dtor = NULL;
  675. }
  676. else if (!op_array) {
  677. /* failed to compile free it, no install */
  678. xc_sandbox_free(&sandbox, 0 TSRMLS_CC);
  679. }
  680. else {
  681. xc_sandbox_free(&sandbox, 1 TSRMLS_CC);
  682. }
  683. ENTER_LOCK(cache) {
  684. cache->compiling = 0;
  685. } LEAVE_LOCK(cache);
  686. if (catched) {
  687. zend_bailout();
  688. }
  689. if (xc_test && stored_xce) {
  690. goto restore;
  691. }
  692. return op_array;
  693. restore:
  694. #ifdef DEBUG
  695. fprintf(stderr, "restoring\n");
  696. #endif
  697. xc_processor_restore_xc_entry_t(&xce, stored_xce, xc_readonly_protection TSRMLS_CC);
  698. op_array = xc_entry_install(&xce, h TSRMLS_CC);
  699. efree(xce.data.php->funcinfos);
  700. efree(xce.data.php->classinfos);
  701. efree(xce.data.php);
  702. #ifdef DEBUG
  703. fprintf(stderr, "restored\n");
  704. #endif
  705. return op_array;
  706. }
  707. /* }}} */
  708. /* gdb helper functions, but N/A for coredump */
  709. int xc_is_rw(const void *p) /* {{{ */
  710. {
  711. int i;
  712. if (!xc_initized) {
  713. return 0;
  714. }
  715. for (i = 0; i < xc_php_hcache.size; i ++) {
  716. if (xc_shm_is_readwrite(xc_php_caches[i]->shm, p)) {
  717. return 1;
  718. }
  719. }
  720. for (i = 0; i < xc_var_hcache.size; i ++) {
  721. if (xc_shm_is_readwrite(xc_var_caches[i]->shm, p)) {
  722. return 1;
  723. }
  724. }
  725. return 0;
  726. }
  727. /* }}} */
  728. int xc_is_ro(const void *p) /* {{{ */
  729. {
  730. int i;
  731. if (!xc_initized) {
  732. return 0;
  733. }
  734. for (i = 0; i < xc_php_hcache.size; i ++) {
  735. if (xc_shm_is_readonly(xc_php_caches[i]->shm, p)) {
  736. return 1;
  737. }
  738. }
  739. for (i = 0; i < xc_var_hcache.size; i ++) {
  740. if (xc_shm_is_readonly(xc_var_caches[i]->shm, p)) {
  741. return 1;
  742. }
  743. }
  744. return 0;
  745. }
  746. /* }}} */
  747. int xc_is_shm(const void *p) /* {{{ */
  748. {
  749. return xc_is_ro(p) || xc_is_rw(p);
  750. }
  751. /* }}} */
  752. /* module helper function */
  753. static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
  754. {
  755. typedef struct {
  756. const char *prefix;
  757. zend_uchar (*getsize)();
  758. const char *(*get)(zend_uchar i);
  759. } xc_meminfo_t;
  760. xc_meminfo_t nameinfos[] = {
  761. { "", xc_get_op_type_count, xc_get_op_type },
  762. { "", xc_get_data_type_count, xc_get_data_type },
  763. { "", xc_get_opcode_count, xc_get_opcode },
  764. { "OPSPEC_", xc_get_op_spec_count, xc_get_op_spec },
  765. { NULL, NULL, NULL }
  766. };
  767. xc_meminfo_t* p;
  768. zend_uchar i, count;
  769. char const_name[96];
  770. int const_name_len;
  771. int undefdone = 0;
  772. for (p = nameinfos; p->getsize; p ++) {
  773. count = p->getsize();
  774. for (i = 0; i < count; i ++) {
  775. const char *name = p->get(i);
  776. if (!name) continue;
  777. if (strcmp(name, "UNDEF") == 0) {
  778. if (undefdone) continue;
  779. undefdone = 1;
  780. }
  781. const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
  782. zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  783. }
  784. }
  785. zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  786. zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  787. zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  788. return 0;
  789. }
  790. /* }}} */
  791. static xc_shm_t *xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache) /* {{{ */
  792. {
  793. int i;
  794. xc_cache_t *cache;
  795. xc_shm_t *shm;
  796. if (!caches) {
  797. return NULL;
  798. }
  799. shm = NULL;
  800. for (i = 0; i < hcache->size; i ++) {
  801. cache = caches[i];
  802. if (cache) {
  803. if (cache->lck) {
  804. xc_lock_destroy(cache->lck);
  805. }
  806. /* do NOT free
  807. if (cache->entries) {
  808. xc_mem_free(cache->mem, cache->entries);
  809. }
  810. xc_mem_free(cache->mem, cache);
  811. */
  812. xc_mem_destroy(cache->mem);
  813. shm = cache->shm;
  814. }
  815. }
  816. free(caches);
  817. return shm;
  818. }
  819. /* }}} */
  820. static xc_cache_t **xc_cache_init(xc_shm_t *shm, char *ptr, xc_hash_t *hcache, xc_hash_t *hentry, xc_shmsize_t shmsize) /* {{{ */
  821. {
  822. xc_cache_t **caches = NULL, *cache;
  823. xc_mem_t *mem;
  824. int i;
  825. xc_memsize_t memsize;
  826. memsize = shmsize / hcache->size;
  827. /* Don't let it break out of mem after ALIGNed
  828. * This is important for
  829. * Simply loop until it fit our need
  830. */
  831. while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
  832. if (memsize < ALIGN(1)) {
  833. CHECK(NULL, "cache too small");
  834. }
  835. memsize --;
  836. }
  837. CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
  838. for (i = 0; i < hcache->size; i ++) {
  839. CHECK(mem = xc_mem_init(ptr, memsize), "Failed init memory allocator");
  840. ptr += memsize;
  841. CHECK(cache = xc_mem_calloc(mem, 1, sizeof(xc_cache_t)), "cache OOM");
  842. CHECK(cache->entries = xc_mem_calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
  843. CHECK(cache->lck = xc_lock_init(NULL), "can't create lock");
  844. cache->hcache = hcache;
  845. cache->hentry = hentry;
  846. cache->shm = shm;
  847. cache->mem = mem;
  848. cache->cacheid = i;
  849. caches[i] = cache;
  850. }
  851. assert(ptr <= (char*)xc_shm_ptr(shm) + shmsize);
  852. return caches;
  853. err:
  854. if (caches) {
  855. xc_cache_destroy(caches, hcache);
  856. }
  857. return NULL;
  858. }
  859. /* }}} */
  860. static void xc_destroy() /* {{{ */
  861. {
  862. xc_shm_t *shm = NULL;
  863. if (origin_compile_file) {
  864. zend_compile_file = origin_compile_file;
  865. origin_compile_file = NULL;
  866. }
  867. if (xc_php_caches) {
  868. shm = xc_cache_destroy(xc_php_caches, &xc_php_hcache);
  869. xc_php_caches = NULL;
  870. }
  871. if (xc_var_caches) {
  872. shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
  873. xc_var_caches = NULL;
  874. }
  875. if (shm) {
  876. xc_shm_destroy(shm);
  877. }
  878. }
  879. /* }}} */
  880. static int xc_init(int module_number TSRMLS_DC) /* {{{ */
  881. {
  882. xc_shm_t *shm;
  883. char *ptr;
  884. xc_php_caches = xc_var_caches = NULL;
  885. if (xc_php_size || xc_var_size) {
  886. CHECK(shm = xc_shm_init(xc_mmap_path, ALIGN(xc_php_size) + ALIGN(xc_var_size), xc_readonly_protection), "Cannot create shm");
  887. if (!xc_shm_can_readonly(shm)) {
  888. xc_readonly_protection = 0;
  889. }
  890. ptr = (char *)xc_shm_ptr(shm);
  891. if (xc_php_size) {
  892. origin_compile_file = zend_compile_file;
  893. zend_compile_file = xc_compile_file;
  894. CHECK(xc_php_caches = xc_cache_init(shm, ptr, &xc_php_hcache, &xc_php_hentry, xc_php_size), "failed init opcode cache");
  895. ptr += ALIGN(xc_php_size);
  896. }
  897. if (xc_var_size) {
  898. CHECK(xc_var_caches = xc_cache_init(shm, ptr, &xc_var_hcache, &xc_var_hentry, xc_var_size), "failed init variable cache");
  899. }
  900. }
  901. return 1;
  902. err:
  903. if (xc_php_caches || xc_var_caches) {
  904. xc_destroy();
  905. /* shm destroied */
  906. }
  907. else if (shm) {
  908. xc_shm_destroy(shm);
  909. }
  910. return 0;
  911. }
  912. /* }}} */
  913. static void xc_request_init(TSRMLS_D) /* {{{ */
  914. {
  915. int i;
  916. if (xc_php_hcache.size && !XG(php_holds)) {
  917. XG(php_holds) = calloc(xc_php_hcache.size, sizeof(xc_stack_t));
  918. for (i = 0; i < xc_php_hcache.size; i ++) {
  919. xc_stack_init(&XG(php_holds[i]));
  920. }
  921. }
  922. if (xc_var_hcache.size && !XG(var_holds)) {
  923. XG(var_holds) = calloc(xc_var_hcache.size, sizeof(xc_stack_t));
  924. for (i = 0; i < xc_var_hcache.size; i ++) {
  925. xc_stack_init(&XG(var_holds[i]));
  926. }
  927. }
  928. if (XG(cacher)) {
  929. #if PHP_API_VERSION <= 20041225
  930. XG(request_time) = time(NULL);
  931. #else
  932. XG(request_time) = sapi_get_request_time(TSRMLS_C);
  933. #endif
  934. }
  935. #ifdef HAVE_XCACHE_COVERAGER
  936. xc_coverager_request_init(TSRMLS_C);
  937. #endif
  938. }
  939. /* }}} */
  940. static void xc_request_shutdown(TSRMLS_D) /* {{{ */
  941. {
  942. xc_entry_unholds(TSRMLS_C);
  943. #ifdef HAVE_XCACHE_COVERAGER
  944. xc_coverager_request_shutdown(TSRMLS_C);
  945. #endif
  946. }
  947. /* }}} */
  948. static void xc_init_globals(zend_xcache_globals* xc_globals TSRMLS_DC) /* {{{ */
  949. {
  950. memset(xc_globals, 0, sizeof(zend_xcache_globals));
  951. }
  952. /* }}} */
  953. static void xc_shutdown_globals(zend_xcache_globals* xc_globals TSRMLS_DC) /* {{{ */
  954. {
  955. int i;
  956. if (xc_globals->php_holds != NULL) {
  957. for (i = 0; i < xc_php_hcache.size; i ++) {
  958. xc_stack_destroy(&xc_globals->php_holds[i]);
  959. }
  960. free(xc_globals->php_holds);
  961. xc_globals->php_holds = NULL;
  962. }
  963. if (xc_globals->var_holds != NULL) {
  964. for (i = 0; i < xc_var_hcache.size; i ++) {
  965. xc_stack_destroy(&xc_globals->var_holds[i]);
  966. }
  967. free(xc_globals->var_holds);
  968. xc_globals->var_holds = NULL;
  969. }
  970. }
  971. /* }}} */
  972. /* user functions */
  973. static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
  974. {
  975. zval **server = NULL;
  976. zval **user = NULL;
  977. zval **pass = NULL;
  978. char *admin_user = NULL;
  979. char *admin_pass = NULL;
  980. HashTable *ht;
  981. if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
  982. admin_user = NULL;
  983. }
  984. if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
  985. admin_pass = NULL;
  986. }
  987. if (admin_user == NULL || admin_pass == NULL) {
  988. php_error_docref(NULL TSRMLS_CC, E_ERROR, "xcache.admin.user and xcache.admin.pass is required");
  989. zend_bailout();
  990. }
  991. if (strlen(admin_pass) != 32) {
  992. php_error_docref(NULL TSRMLS_CC, E_ERROR, "unexpect %d bytes of xcache.admin.pass, expected 32 bytes, the password after md5()", strlen(admin_pass));
  993. zend_bailout();
  994. }
  995. if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
  996. php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
  997. zend_bailout();
  998. }
  999. ht = HASH_OF((*server));
  1000. if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
  1001. user = NULL;
  1002. }
  1003. else if (Z_TYPE_PP(user) != IS_STRING) {
  1004. user = NULL;
  1005. }
  1006. if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
  1007. pass = NULL;
  1008. }
  1009. else if (Z_TYPE_PP(pass) != IS_STRING) {
  1010. pass = NULL;
  1011. }
  1012. if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
  1013. PHP_MD5_CTX context;
  1014. char md5str[33];
  1015. unsigned char digest[16];
  1016. PHP_MD5Init(&context);
  1017. PHP_MD5Update(&context, Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
  1018. PHP_MD5Final(digest, &context);
  1019. md5str[0] = '\0';
  1020. make_digest(md5str, digest);
  1021. if (strcmp(admin_pass, md5str) == 0) {
  1022. return 1;
  1023. }
  1024. }
  1025. #define STR "WWW-authenticate: basic realm='XCache Administration'"
  1026. sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
  1027. #undef STR
  1028. #define STR "HTTP/1.0 401 Unauthorized"
  1029. sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
  1030. #undef STR
  1031. ZEND_PUTS("XCache Auth Failed. User and Password is case sense\n");
  1032. zend_bailout();
  1033. return 0;
  1034. }
  1035. /* }}} */
  1036. /* {{{ xcache_admin_operate */
  1037. typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
  1038. static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
  1039. {
  1040. long type;
  1041. int size;
  1042. xc_cache_t **caches, *cache;
  1043. long id = 0;
  1044. if (!xc_initized) {
  1045. php_error_docref(NULL TSRMLS_CC, E_WARNING, "XCache is not initized");
  1046. RETURN_FALSE;
  1047. }
  1048. xcache_admin_auth_check(TSRMLS_C);
  1049. if (optype == XC_OP_COUNT) {
  1050. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
  1051. return;
  1052. }
  1053. }
  1054. else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
  1055. return;
  1056. }
  1057. switch (type) {
  1058. case XC_TYPE_PHP:
  1059. size = xc_php_hcache.size;
  1060. caches = xc_php_caches;
  1061. break;
  1062. case XC_TYPE_VAR:
  1063. size = xc_var_hcache.size;
  1064. caches = xc_var_caches;
  1065. break;
  1066. default:
  1067. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
  1068. RETURN_FALSE;
  1069. }
  1070. switch (optype) {
  1071. case XC_OP_COUNT:
  1072. RETURN_LONG(size)
  1073. break;
  1074. case XC_OP_INFO:
  1075. case XC_OP_LIST:
  1076. if (id < 0 || id >= size) {
  1077. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
  1078. RETURN_FALSE;
  1079. }
  1080. array_init(return_value);
  1081. cache = caches[id];
  1082. ENTER_LOCK(cache) {
  1083. if (optype == XC_OP_INFO) {
  1084. xc_fillinfo_dmz(cache, return_value TSRMLS_CC);
  1085. }
  1086. else {
  1087. xc_filllist_dmz(cache, return_value TSRMLS_CC);
  1088. }
  1089. } LEAVE_LOCK(cache);
  1090. break;
  1091. case XC_OP_CLEAR:
  1092. {
  1093. xc_entry_t *e, *next;
  1094. int i, c;
  1095. if (id < 0 || id >= size) {
  1096. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
  1097. RETURN_FALSE;
  1098. }
  1099. cache = caches[id];
  1100. ENTER_LOCK(cache) {
  1101. for (i = 0, c = cache->hentry->size; i < c; i ++) {
  1102. for (e = cache->entries[i]; e; e = next) {
  1103. next = e->next;
  1104. xc_entry_remove_dmz(e TSRMLS_CC);
  1105. }
  1106. cache->entries[i] = NULL;
  1107. }
  1108. } LEAVE_LOCK(cache);
  1109. xc_entry_gc(TSRMLS_C);
  1110. }
  1111. break;
  1112. default:
  1113. assert(0);
  1114. }
  1115. }
  1116. /* }}} */
  1117. /* {{{ proto int xcache_count(int type)
  1118. Return count of cache on specified cache type */
  1119. PHP_FUNCTION(xcache_count)
  1120. {
  1121. xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1122. }
  1123. /* }}} */
  1124. /* {{{ proto array xcache_info(int type, int id)
  1125. Get cache info by id on specified cache type */
  1126. PHP_FUNCTION(xcache_info)
  1127. {
  1128. xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1129. }
  1130. /* }}} */
  1131. /* {{{ proto array xcache_list(int type, int id)
  1132. Get cache entries list by id on specified cache type */
  1133. PHP_FUNCTION(xcache_list)
  1134. {
  1135. xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1136. }
  1137. /* }}} */
  1138. /* {{{ proto array xcache_clear_cache(int type, int id)
  1139. Clear cache by id on specified cache type */
  1140. PHP_FUNCTION(xcache_clear_cache)
  1141. {
  1142. xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1143. }
  1144. /* }}} */
  1145. static int xc_entry_init_key_var(xc_entry_t *xce, zval *name TSRMLS_DC) /* {{{ */
  1146. {
  1147. xc_hash_value_t hv;
  1148. int cacheid;
  1149. switch (Z_TYPE_P(name)) {
  1150. #ifdef IS_UNICODE
  1151. case IS_UNICODE:
  1152. #endif
  1153. case IS_STRING:
  1154. break;
  1155. default:
  1156. #ifdef IS_UNICODE
  1157. convert_to_text(name);
  1158. #else
  1159. convert_to_string(name);
  1160. #endif
  1161. }
  1162. #ifdef IS_UNICODE
  1163. xce->name_type = name->type;
  1164. #endif
  1165. xce->name = name->value;
  1166. hv = xc_entry_hash_var(xce);
  1167. cacheid = (hv & xc_var_hcache.mask);
  1168. xce->cache = xc_var_caches[cacheid];
  1169. hv >>= xc_var_hcache.bits;
  1170. xce->hvalue = (hv & xc_var_hentry.mask);
  1171. xce->type = XC_TYPE_VAR;
  1172. return SUCCESS;
  1173. }
  1174. /* }}} */
  1175. #define TIME_MAX (sizeof(time_t) == sizeof(long) ? LONG_MAX : INT_MAX)
  1176. /* {{{ proto mixed xcache_get(string name)
  1177. Get cached data by specified name */
  1178. PHP_FUNCTION(xcache_get)
  1179. {
  1180. xc_entry_t xce, *stored_xce;
  1181. xc_entry_data_var_t var;
  1182. zval *name;
  1183. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
  1184. return;
  1185. }
  1186. xce.data.var = &var;
  1187. xc_entry_init_key_var(&xce, name TSRMLS_CC);
  1188. ENTER_LOCK(xce.cache) {
  1189. stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
  1190. if (stored_xce) {
  1191. if (XG(request_time) <= stored_xce->data.var->etime) {
  1192. xc_processor_restore_zval(return_value, stored_xce->data.var->value TSRMLS_CC);
  1193. /* return */
  1194. break;
  1195. }
  1196. else {
  1197. xc_entry_remove_dmz(stored_xce TSRMLS_CC);
  1198. }
  1199. }
  1200. RETVAL_NULL();
  1201. } LEAVE_LOCK(xce.cache);
  1202. }
  1203. /* }}} */
  1204. /* {{{ proto bool xcache_set(string name, mixed value [, int ttl])
  1205. Store data to cache by specified name */
  1206. PHP_FUNCTION(xcache_set)
  1207. {
  1208. xc_entry_t xce, *stored_xce;
  1209. xc_entry_data_var_t var;
  1210. zval *name;
  1211. zval *value;
  1212. long ttl = 0;
  1213. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &ttl) == FAILURE) {
  1214. return;
  1215. }
  1216. xce.data.var = &var;
  1217. xc_entry_init_key_var(&xce, name TSRMLS_CC);
  1218. ENTER_LOCK(xce.cache) {
  1219. stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
  1220. if (stored_xce) {
  1221. xc_entry_remove_dmz(stored_xce TSRMLS_CC);
  1222. }
  1223. var.value = value;
  1224. var.etime = ttl ? XG(request_time) + ttl : TIME_MAX;
  1225. RETVAL_BOOL(xc_entry_store_dmz(&xce TSRMLS_CC) != NULL ? 1 : 0);
  1226. } LEAVE_LOCK(xce.cache);
  1227. }
  1228. /* }}} */
  1229. /* {{{ proto bool xcache_isset(string name)
  1230. Check if an entry exists in cache by specified name */
  1231. PHP_FUNCTION(xcache_isset)
  1232. {
  1233. xc_entry_t xce, *stored_xce;
  1234. xc_entry_data_var_t var;
  1235. zval *name;
  1236. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
  1237. return;
  1238. }
  1239. xce.data.var = &var;
  1240. xc_entry_init_key_var(&xce, name TSRMLS_CC);
  1241. ENTER_LOCK(xce.cache) {
  1242. stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
  1243. if (stored_xce) {
  1244. if (XG(request_time) <= stored_xce->data.var->etime) {
  1245. RETVAL_TRUE;
  1246. /* return */
  1247. break;
  1248. }
  1249. else {
  1250. xc_entry_remove_dmz(stored_xce TSRMLS_CC);
  1251. }
  1252. }
  1253. RETVAL_FALSE;
  1254. } LEAVE_LOCK(xce.cache);
  1255. }
  1256. /* }}} */
  1257. /* {{{ proto bool xcache_unset(string name)
  1258. Unset existing data in cache by specified name */
  1259. PHP_FUNCTION(xcache_unset)
  1260. {
  1261. xc_entry_t xce, *stored_xce;
  1262. xc_entry_data_var_t var;
  1263. zval *name;
  1264. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
  1265. return;
  1266. }
  1267. xce.data.var = &var;
  1268. xc_entry_init_key_var(&xce, name TSRMLS_CC);
  1269. ENTER_LOCK(xce.cache) {
  1270. stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
  1271. if (stored_xce) {
  1272. xc_entry_remove_dmz(stored_xce TSRMLS_CC);
  1273. RETVAL_TRUE;
  1274. }
  1275. else {
  1276. RETVAL_FALSE;
  1277. }
  1278. } LEAVE_LOCK(xce.cache);
  1279. }
  1280. /* }}} */
  1281. static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
  1282. {
  1283. xc_entry_t xce, *stored_xce;
  1284. xc_entry_data_var_t var, *stored_var;
  1285. zval *name;
  1286. long count = 1;
  1287. long ttl = 0;
  1288. long value = 0;
  1289. zval oldzval;
  1290. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &ttl) == FAILURE) {
  1291. return;
  1292. }
  1293. xce.data.var = &var;
  1294. xc_entry_init_key_var(&xce, name TSRMLS_CC);
  1295. ENTER_LOCK(xce.cache) {
  1296. stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
  1297. if (stored_xce) {
  1298. #ifdef DEBUG
  1299. fprintf(stderr, "incdec: gotxce %s\n", xce.name.str.val);
  1300. #endif
  1301. stored_var = stored_xce->data.var;
  1302. /* timeout */
  1303. if (XG(request_time) > stored_var->etime) {
  1304. #ifdef DEBUG
  1305. fprintf(stderr, "incdec: expired\n");
  1306. #endif
  1307. xc_entry_remove_dmz(stored_xce TSRMLS_CC);
  1308. stored_xce = NULL;
  1309. }
  1310. else {
  1311. /* do it in place */
  1312. if (Z_TYPE_P(stored_var->value) == IS_LONG) {
  1313. #ifdef DEBUG
  1314. fprintf(stderr, "incdec: islong\n");
  1315. #endif
  1316. value = Z_LVAL_P(stored_var->value);
  1317. value += (inc == 1 ? count : - count);
  1318. RETVAL_LONG(value);
  1319. Z_LVAL_P(stored_var->value) = value;
  1320. break;
  1321. }
  1322. else {
  1323. #ifdef DEBUG
  1324. fprintf(stderr, "incdec: notlong\n");
  1325. #endif
  1326. xc_processor_restore_zval(&oldzval, stored_xce->data.var->value TSRMLS_CC);
  1327. convert_to_long(&oldzval);
  1328. value = Z_LVAL(oldzval);
  1329. zval_dtor(&oldzval);
  1330. }
  1331. }
  1332. }
  1333. #ifdef DEBUG
  1334. else {
  1335. fprintf(stderr, "incdec: %s not found\n", xce.name.str.val);
  1336. }
  1337. #endif
  1338. value += (inc == 1 ? count : - count);
  1339. RETVAL_LONG(value);
  1340. var.value = return_value;
  1341. var.etime = ttl ? XG(request_time) + ttl : TIME_MAX;
  1342. if (stored_xce) {
  1343. xce.atime = stored_xce->atime;
  1344. xce.ctime = stored_xce->ctime;
  1345. xce.hits = stored_xce->hits;
  1346. xc_entry_remove_dmz(stored_xce TSRMLS_CC);
  1347. }
  1348. xc_entry_store_dmz(&xce TSRMLS_CC);
  1349. } LEAVE_LOCK(xce.cache);
  1350. }
  1351. /* }}} */
  1352. /* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
  1353. Increase an int counter in cache by specified name, create it if not exists */
  1354. PHP_FUNCTION(xcache_inc)
  1355. {
  1356. xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1357. }
  1358. /* }}} */
  1359. /* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
  1360. Decrease an int counter in cache by specified name, create it if not exists */
  1361. PHP_FUNCTION(xcache_dec)
  1362. {
  1363. xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1364. }
  1365. /* }}} */
  1366. /* {{{ proto string xcache_asm(string filename)
  1367. */
  1368. #ifdef HAVE_XCACHE_ASSEMBLER
  1369. PHP_FUNCTION(xcache_asm)
  1370. {
  1371. }
  1372. #endif
  1373. /* }}} */
  1374. #ifdef HAVE_XCACHE_DISASSEMBLER
  1375. /* {{{ proto array xcache_dasm_file(string filename)
  1376. Disassemble file into opcode array by filename */
  1377. PHP_FUNCTION(xcache_dasm_file)
  1378. {
  1379. char *filename;
  1380. int filename_len;
  1381. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
  1382. return;
  1383. }
  1384. if (!filename_len) RETURN_FALSE;
  1385. xc_dasm_file(return_value, filename TSRMLS_CC);
  1386. }
  1387. /* }}} */
  1388. /* {{{ proto array xcache_dasm_string(string code)
  1389. Disassemble php code into opcode array */
  1390. PHP_FUNCTION(xcache_dasm_string)
  1391. {
  1392. zval *code;
  1393. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
  1394. return;
  1395. }
  1396. xc_dasm_string(return_value, code TSRMLS_CC);
  1397. }
  1398. /* }}} */
  1399. #endif
  1400. /* {{{ proto string xcache_encode(string filename)
  1401. Encode php file into XCache opcode encoded format */
  1402. #ifdef HAVE_XCACHE_ENCODER
  1403. PHP_FUNCTION(xcache_encode)
  1404. {
  1405. }
  1406. #endif
  1407. /* }}} */
  1408. /* {{{ proto bool xcache_decode_file(string filename)
  1409. Decode(load) opcode from XCache encoded format file */
  1410. #ifdef HAVE_XCACHE_DECODER
  1411. PHP_FUNCTION(xcache_decode_file)
  1412. {
  1413. }
  1414. #endif
  1415. /* }}} */
  1416. /* {{{ proto bool xcache_decode_string(string data)
  1417. Decode(load) opcode from XCache encoded format data */
  1418. #ifdef HAVE_XCACHE_DECODER
  1419. PHP_FUNCTION(xcache_decode_string)
  1420. {
  1421. }
  1422. #endif
  1423. /* }}} */
  1424. /* {{{ xc_call_getter */
  1425. typedef const char *(xc_name_getter_t)(zend_uchar type);
  1426. static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
  1427. {
  1428. long spec;
  1429. const char *name;
  1430. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
  1431. return;
  1432. }
  1433. if (spec >= 0 && spec < count) {
  1434. name = getter((zend_uchar) spec);
  1435. if (name) {
  1436. /* RETURN_STRING */
  1437. int len = strlen(name);
  1438. return_value->value.str.len = len;
  1439. return_value->value.str.val = estrndup(name, len);
  1440. return_value->type = IS_STRING;
  1441. return;
  1442. }
  1443. }
  1444. RETURN_NULL();
  1445. }
  1446. /* }}} */
  1447. /* {{{ proto string xcache_get_op_type(int op_type) */
  1448. PHP_FUNCTION(xcache_get_op_type)
  1449. {
  1450. xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1451. }
  1452. /* }}} */
  1453. /* {{{ proto string xcache_get_data_type(int type) */
  1454. PHP_FUNCTION(xcache_get_data_type)
  1455. {
  1456. xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1457. }
  1458. /* }}} */
  1459. /* {{{ proto string xcache_get_opcode(int opcode) */
  1460. PHP_FUNCTION(xcache_get_opcode)
  1461. {
  1462. xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1463. }
  1464. /* }}} */
  1465. /* {{{ proto string xcache_get_op_spec(int op_type) */
  1466. PHP_FUNCTION(xcache_get_op_spec)
  1467. {
  1468. xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  1469. }
  1470. /* }}} */
  1471. #ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
  1472. /* {{{ proto string xcache_get_opcode_spec(int opcode) */
  1473. PHP_FUNCTION(xcache_get_opcode_spec)
  1474. {
  1475. long spec;
  1476. const xc_opcode_spec_t *opspec;
  1477. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
  1478. return;
  1479. }
  1480. if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
  1481. opspec = xc_get_opcode_spec((zend_uchar) spec);
  1482. if (opspec) {
  1483. array_init(return_value);
  1484. add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
  1485. add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
  1486. add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
  1487. add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
  1488. return;
  1489. }
  1490. }
  1491. RETURN_NULL();
  1492. }
  1493. /* }}} */
  1494. #endif
  1495. /* {{{ proto mixed xcache_get_special_value(zval value) */
  1496. PHP_FUNCTION(xcache_get_special_value)
  1497. {
  1498. zval *value;
  1499. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  1500. return;
  1501. }
  1502. if (value->type == IS_CONSTANT) {
  1503. *return_value = *value;
  1504. zval_copy_ctor(return_value);
  1505. return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
  1506. return;
  1507. }
  1508. if (value->type == IS_CONSTANT_ARRAY) {
  1509. *return_value = *value;
  1510. zval_copy_ctor(return_value);
  1511. return_value->type = IS_ARRAY;
  1512. return;
  1513. }
  1514. RETURN_NULL();
  1515. }
  1516. /* }}} */
  1517. /* {{{ proto string xcache_coredump(int op_type) */
  1518. PHP_FUNCTION(xcache_coredump)
  1519. {
  1520. if (xc_test) {
  1521. raise(SIGSEGV);
  1522. }
  1523. else {
  1524. php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
  1525. }
  1526. }
  1527. /* }}} */
  1528. /* {{{ proto string xcache_is_autoglobal(string name) */
  1529. PHP_FUNCTION(xcache_is_autoglobal)
  1530. {
  1531. char *name;
  1532. int name_len;
  1533. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
  1534. return;
  1535. }
  1536. RETURN_BOOL(zend_hash_exists(CG(auto_globals), name, name_len + 1));
  1537. }
  1538. /* }}} */
  1539. static function_entry xcache_functions[] = /* {{{ */
  1540. {
  1541. PHP_FE(xcache_count, NULL)
  1542. PHP_FE(xcache_info, NULL)
  1543. PHP_FE(xcache_list, NULL)
  1544. PHP_FE(xcache_clear_cache, NULL)
  1545. PHP_FE(xcache_coredump, NULL)
  1546. #ifdef HAVE_XCACHE_ASSEMBLER
  1547. PHP_FE(xcache_asm, NULL)
  1548. #endif
  1549. #ifdef HAVE_XCACHE_DISASSEMBLER
  1550. PHP_FE(xcache_dasm_file, NULL)
  1551. PHP_FE(xcache_dasm_string, NULL)
  1552. #endif
  1553. #ifdef HAVE_XCACHE_ENCODER
  1554. PHP_FE(xcache_encode, NULL)
  1555. #endif
  1556. #ifdef HAVE_XCACHE_DECODER
  1557. PHP_FE(xcache_decode_file, NULL)
  1558. PHP_FE(xcache_decode_string, NULL)
  1559. #endif
  1560. #ifdef HAVE_XCACHE_COVERAGER
  1561. PHP_FE(xcache_coverager_decode, NULL)
  1562. #endif
  1563. PHP_FE(xcache_get_special_value, NULL)
  1564. PHP_FE(xcache_get_op_type, NULL)
  1565. PHP_FE(xcache_get_data_type, NULL)
  1566. PHP_FE(xcache_get_opcode, NULL)
  1567. #ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
  1568. PHP_FE(xcache_get_opcode_spec, NULL)
  1569. #endif
  1570. PHP_FE(xcache_is_autoglobal, NULL)
  1571. PHP_FE(xcache_inc, NULL)
  1572. PHP_FE(xcache_dec, NULL)
  1573. PHP_FE(xcache_get, NULL)
  1574. PHP_FE(xcache_set, NULL)
  1575. PHP_FE(xcache_isset, NULL)
  1576. PHP_FE(xcache_unset, NULL)
  1577. {NULL, NULL, NULL}
  1578. };
  1579. /* }}} */
  1580. /* signal handler */
  1581. static void (*original_sigsegv_handler)(int) = NULL;
  1582. static void xcache_sigsegv_handler(int sig) /* {{{ */
  1583. {
  1584. if (original_sigsegv_handler != xcache_sigsegv_handler) {
  1585. signal(SIGSEGV, original_sigsegv_handler);
  1586. }
  1587. else {
  1588. signal(SIGSEGV, SIG_DFL);
  1589. }
  1590. if (xc_coredump_dir && xc_coredump_dir[0]) {
  1591. chdir(xc_coredump_dir);
  1592. }
  1593. raise(sig);
  1594. }
  1595. /* }}} */
  1596. /* {{{ PHP_INI */
  1597. static PHP_INI_MH(xc_OnUpdateBool)
  1598. {
  1599. zend_bool *p = (zend_bool *)mh_arg1;
  1600. if (strncasecmp("on", new_value, sizeof("on"))) {
  1601. *p = (zend_bool) atoi(new_value);
  1602. }
  1603. else {
  1604. *p = (zend_bool) 1;
  1605. }
  1606. return SUCCESS;
  1607. }
  1608. static PHP_INI_MH(xc_OnUpdateString)
  1609. {
  1610. char **p = (char**)mh_arg1;
  1611. if (*p) {
  1612. pefree(*p, 1);
  1613. }
  1614. *p = pemalloc(strlen(new_value) + 1, 1);
  1615. strcpy(*p, new_value);
  1616. return SUCCESS;
  1617. }
  1618. #ifdef ZEND_ENGINE_2
  1619. #define OnUpdateInt OnUpdateLong
  1620. #endif
  1621. #ifdef ZEND_WIN32
  1622. # define DEFAULT_PATH "xcache"
  1623. #else
  1624. # define DEFAULT_PATH "/dev/zero"
  1625. #endif
  1626. PHP_INI_BEGIN()
  1627. PHP_INI_ENTRY1 ("xcache.mmap_path", DEFAULT_PATH, PHP_INI_SYSTEM, xc_OnUpdateString, &xc_mmap_path)
  1628. PHP_INI_ENTRY1 ("xcache.coredump_directory", "", PHP_INI_SYSTEM, xc_OnUpdateString, &xc_coredump_dir)
  1629. PHP_INI_ENTRY1 ("xcache.test", "0", PHP_INI_SYSTEM, xc_OnUpdateBool, &xc_test)
  1630. PHP_INI_ENTRY1 ("xcache.readonly_protection", "0", PHP_INI_SYSTEM, xc_OnUpdateBool, &xc_readonly_protection)
  1631. STD_PHP_INI_BOOLEAN("xcache.cacher", "1", PHP_INI_ALL, OnUpdateBool, cacher, zend_xcache_globals, xcache_globals)
  1632. #ifdef HAVE_XCACHE_OPTIMIZER
  1633. STD_PHP_INI_BOOLEAN("xcache.optimizer", "0", PHP_INI_ALL, OnUpdateBool, optimizer, zend_xcache_globals, xcache_globals)
  1634. #endif
  1635. #ifdef HAVE_XCACHE_COVERAGER
  1636. PHP_INI_ENTRY1 ("xcache.coveragedump_directory", "/tmp/pcov/", PHP_INI_SYSTEM, xc_OnUpdateString, &xc_coveragedump_dir)
  1637. STD_PHP_INI_BOOLEAN("xcache.coveragedumper" , "0", PHP_INI_ALL, OnUpdateBool, coveragedumper, zend_xcache_globals, xcache_globals)
  1638. #endif
  1639. PHP_INI_END()
  1640. /* }}} */
  1641. static int xc_config_long_disp(char *name, char *default_value) /* {{{ */
  1642. {
  1643. char *value;
  1644. char buf[100];
  1645. if (cfg_get_string(name, &value) != SUCCESS) {
  1646. sprintf(buf, "%s (default)", default_value);
  1647. php_info_print_table_row(2, name, buf);
  1648. }
  1649. else {
  1650. php_info_print_table_row(2, name, value);
  1651. }
  1652. return SUCCESS;
  1653. }
  1654. /* }}} */
  1655. #define xc_config_hash_disp xc_config_long_disp
  1656. /* {{{ PHP_MINFO_FUNCTION(xcache) */
  1657. static PHP_MINFO_FUNCTION(xcache)
  1658. {
  1659. char buf[100];
  1660. char *ptr;
  1661. php_info_print_table_start();
  1662. php_info_print_table_header(2, "XCache Support", (xc_php_size || xc_var_size) ? "enabled" : "disabled");
  1663. php_info_print_table_row(2, "Version", XCACHE_VERSION);
  1664. php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
  1665. php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A or disabled");
  1666. if (xc_php_size) {
  1667. ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
  1668. sprintf(buf, "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
  1669. php_info_print_table_row(2, "Opcode Cache", buf);
  1670. efree(ptr);
  1671. }
  1672. else {
  1673. php_info_print_table_row(2, "Opcode Cache", "disabled");
  1674. }
  1675. if (xc_var_size) {
  1676. ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
  1677. sprintf(buf, "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
  1678. php_info_print_table_row(2, "Variable Cache", buf);
  1679. efree(ptr);
  1680. }
  1681. else {
  1682. php_info_print_table_row(2, "Variable Cache", "disabled");
  1683. }
  1684. #ifdef HAVE_XCACHE_COVERAGER
  1685. php_info_print_table_row(2, "Coverage Dumper", XG(coveragedumper) && xc_coveragedump_dir && xc_coveragedump_dir[0] ? "enabled" : "disabled");
  1686. #endif
  1687. php_info_print_table_end();
  1688. php_info_print_table_start();
  1689. php_info_print_table_header(2, "Directive ", "Value");
  1690. xc_config_long_disp("xcache.size", "0");
  1691. xc_config_hash_disp("xcache.count", "1");
  1692. xc_config_hash_disp("xcache.slots", "8K");
  1693. xc_config_long_disp("xcache.var_size", "0");
  1694. xc_config_hash_disp("xcache.var_count", "1");
  1695. xc_config_hash_disp("xcache.var_slots", "8K");
  1696. php_info_print_table_end();
  1697. DISPLAY_INI_ENTRIES();
  1698. }
  1699. /* }}} */
  1700. /* {{{ extension startup */
  1701. static void xc_zend_extension_register(zend_extension *new_extension, DL_HANDLE handle)
  1702. {
  1703. zend_extension extension;
  1704. extension = *new_extension;
  1705. extension.handle = handle;
  1706. zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
  1707. zend_llist_add_element(&zend_extensions, &extension);
  1708. #ifdef DEBUG
  1709. fprintf(stderr, "registered\n");
  1710. #endif
  1711. }
  1712. /* dirty check */
  1713. #if defined(COMPILE_DL_XCACHE) && (defined(ZEND_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__))
  1714. # define zend_append_version_info(x) do { } while (0)
  1715. #else
  1716. extern void zend_append_version_info(zend_extension *extension);
  1717. #endif
  1718. static int xc_zend_extension_startup(zend_extension *extension)
  1719. {
  1720. if (extension->startup) {
  1721. if (extension->startup(extension) != SUCCESS) {
  1722. return FAILURE;
  1723. }
  1724. zend_append_version_info(extension);
  1725. }
  1726. return SUCCESS;
  1727. }
  1728. /* }}} */
  1729. static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
  1730. {
  1731. int bits, size;
  1732. char *value;
  1733. if (cfg_get_string(name, &value) != SUCCESS) {
  1734. value = default_value;
  1735. }
  1736. p->size = zend_atoi(value, strlen(value));
  1737. for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
  1738. /* empty body */
  1739. }
  1740. p->size = size;
  1741. p->bits = bits;
  1742. p->mask = size - 1;
  1743. return SUCCESS;
  1744. }
  1745. /* }}} */
  1746. static int xc_config_long(long *p, char *name, char *default_value) /* {{{ */
  1747. {
  1748. char *value;
  1749. if (cfg_get_string(name, &value) != SUCCESS) {
  1750. value = default_value;
  1751. }
  1752. *p = zend_atoi(value, strlen(value));
  1753. return SUCCESS;
  1754. }
  1755. /* }}} */
  1756. /* {{{ PHP_MINIT_FUNCTION(xcache) */
  1757. static PHP_MINIT_FUNCTION(xcache)
  1758. {
  1759. char *env;
  1760. xc_module_gotup = 1;
  1761. if (!xc_zend_extension_gotup) {
  1762. if (zend_get_extension(XCACHE_NAME) == NULL) {
  1763. xc_zend_extension_register(&zend_extension_entry, 0);
  1764. xc_zend_extension_startup(&zend_extension_entry);
  1765. }
  1766. }
  1767. ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
  1768. REGISTER_INI_ENTRIES();
  1769. if (strcmp(sapi_module.name, "cli") == 0) {
  1770. if ((env = getenv("XCACHE_TEST")) != NULL) {
  1771. zend_alter_ini_entry("xcache.test", sizeof("xcache.test"), env, strlen(env) + 1, PHP_INI_SYSTEM, PHP_INI_STAGE_STARTUP);
  1772. }
  1773. if (!xc_test) {
  1774. /* disable cache for cli except for test */
  1775. xc_php_size = xc_var_size = 0;
  1776. }
  1777. }
  1778. xc_config_long(&xc_php_size, "xcache.size", "0");
  1779. xc_config_hash(&xc_php_hcache, "xcache.count", "1");
  1780. xc_config_hash(&xc_php_hentry, "xcache.slots", "8K");
  1781. xc_config_long(&xc_var_size, "xcache.var_size", "0");
  1782. xc_config_hash(&xc_var_hcache, "xcache.var_count", "1");
  1783. xc_config_hash(&xc_var_hentry, "xcache.var_slots", "8K");
  1784. if (xc_php_size <= 0) {
  1785. xc_php_size = xc_php_hcache.size = 0;
  1786. }
  1787. if (xc_var_size <= 0) {
  1788. xc_var_size = xc_var_hcache.size = 0;
  1789. }
  1790. if (xc_coredump_dir && xc_coredump_dir[0]) {
  1791. original_sigsegv_handler = signal(SIGSEGV, xcache_sigsegv_handler);
  1792. }
  1793. xc_init_constant(module_number TSRMLS_CC);
  1794. if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
  1795. if (!xc_init(module_number TSRMLS_CC)) {
  1796. zend_error(E_ERROR, "XCache: Cannot init");
  1797. goto err_init;
  1798. }
  1799. xc_initized = 1;
  1800. }
  1801. #ifdef HAVE_XCACHE_COVERAGER
  1802. xc_coverager_init(module_number TSRMLS_CC);
  1803. #endif
  1804. return SUCCESS;
  1805. err_init:
  1806. return FAILURE;
  1807. }
  1808. /* }}} */
  1809. /* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
  1810. static PHP_MSHUTDOWN_FUNCTION(xcache)
  1811. {
  1812. if (xc_initized) {
  1813. xc_destroy();
  1814. xc_initized = 0;
  1815. }
  1816. if (xc_mmap_path) {
  1817. pefree(xc_mmap_path, 1);
  1818. xc_mmap_path = NULL;
  1819. }
  1820. #ifdef HAVE_XCACHE_COVERAGER
  1821. xc_coverager_destroy();
  1822. #endif
  1823. if (xc_coredump_dir && xc_coredump_dir[0]) {
  1824. signal(SIGSEGV, original_sigsegv_handler);
  1825. }
  1826. if (xc_coredump_dir) {
  1827. pefree(xc_coredump_dir, 1);
  1828. xc_coredump_dir = NULL;
  1829. }
  1830. #ifdef ZTS
  1831. ts_free_id(xcache_globals_id);
  1832. #else
  1833. xc_shutdown_globals(&xcache_globals TSRMLS_CC);
  1834. #endif
  1835. UNREGISTER_INI_ENTRIES();
  1836. return SUCCESS;
  1837. }
  1838. /* }}} */
  1839. /* {{{ PHP_RINIT_FUNCTION(xcache) */
  1840. static PHP_RINIT_FUNCTION(xcache)
  1841. {
  1842. xc_request_init(TSRMLS_C);
  1843. return SUCCESS;
  1844. }
  1845. /* }}} */
  1846. /* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
  1847. #ifndef ZEND_ENGINE_2
  1848. static PHP_RSHUTDOWN_FUNCTION(xcache)
  1849. #else
  1850. static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
  1851. #endif
  1852. {
  1853. #ifdef ZEND_ENGINE_2
  1854. TSRMLS_FETCH();
  1855. #endif
  1856. xc_request_shutdown(TSRMLS_C);
  1857. return SUCCESS;
  1858. }
  1859. /* }}} */
  1860. /* {{{ module definition structure */
  1861. zend_module_entry xcache_module_entry = {
  1862. STANDARD_MODULE_HEADER,
  1863. "XCache",
  1864. xcache_functions,
  1865. PHP_MINIT(xcache),
  1866. PHP_MSHUTDOWN(xcache),
  1867. PHP_RINIT(xcache),
  1868. #ifndef ZEND_ENGINE_2
  1869. PHP_RSHUTDOWN(xcache),
  1870. #else
  1871. NULL,
  1872. #endif
  1873. PHP_MINFO(xcache),
  1874. XCACHE_VERSION,
  1875. #ifdef ZEND_ENGINE_2
  1876. ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
  1877. #else
  1878. NULL,
  1879. NULL,
  1880. #endif
  1881. STANDARD_MODULE_PROPERTIES_EX
  1882. };
  1883. #ifdef COMPILE_DL_XCACHE
  1884. ZEND_GET_MODULE(xcache)
  1885. #endif
  1886. /* }}} */
  1887. ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
  1888. {
  1889. if (xc_zend_extension_gotup) {
  1890. return FAILURE;
  1891. }
  1892. xc_zend_extension_gotup = 1;
  1893. if (!xc_module_gotup) {
  1894. return zend_startup_module(&xcache_module_entry);
  1895. }
  1896. return SUCCESS;
  1897. }
  1898. /* }}} */
  1899. ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
  1900. {
  1901. /* empty */
  1902. }
  1903. /* }}} */
  1904. ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
  1905. {
  1906. #ifdef HAVE_XCACHE_COVERAGER
  1907. xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
  1908. #endif
  1909. }
  1910. /* }}} */
  1911. ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
  1912. {
  1913. #if 0
  1914. xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
  1915. #endif
  1916. }
  1917. /* }}} */
  1918. ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
  1919. {
  1920. #if 0
  1921. xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
  1922. #endif
  1923. }
  1924. /* }}} */
  1925. /* {{{ zend extension definition structure */
  1926. ZEND_DLEXPORT zend_extension zend_extension_entry = {
  1927. XCACHE_NAME,
  1928. XCACHE_VERSION,
  1929. XCACHE_AUTHOR,
  1930. XCACHE_URL,
  1931. XCACHE_COPYRIGHT,
  1932. xcache_zend_startup,
  1933. xcache_zend_shutdown,
  1934. NULL, /* activate_func_t */
  1935. NULL, /* deactivate_func_t */
  1936. NULL, /* message_handler_func_t */
  1937. NULL, /* op_array_handler_func_t */
  1938. xcache_statement_handler,
  1939. xcache_fcall_begin_handler,
  1940. xcache_fcall_end_handler,
  1941. NULL, /* op_array_ctor_func_t */
  1942. NULL, /* op_array_dtor_func_t */
  1943. STANDARD_ZEND_EXTENSION_PROPERTIES
  1944. };
  1945. #ifndef ZEND_EXT_API
  1946. # define ZEND_EXT_API ZEND_DLEXPORT
  1947. #endif
  1948. #if COMPILE_DL_XCACHE
  1949. ZEND_EXTENSION();
  1950. #endif
  1951. /* }}} */