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.

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