source: branches/1.3/xcache.c @ 622

Last change on this file since 622 was 622, checked in by moo, 5 years ago

merged r504:513 from trunk

  • Property svn:eol-style set to native
File size: 74.7 KB
Line 
1
2#if 0
3#define DEBUG
4#endif
5
6#if 0
7#define SHOW_DPRINT
8#endif
9
10/* {{{ macros */
11#include <stdlib.h>
12#include <stdio.h>
13#include <string.h>
14
15#include <signal.h>
16
17#include "php.h"
18#include "ext/standard/info.h"
19#include "ext/standard/md5.h"
20#include "ext/standard/php_math.h"
21#include "zend_extensions.h"
22#include "SAPI.h"
23
24#include "xcache.h"
25#include "optimizer.h"
26#include "coverager.h"
27#include "disassembler.h"
28#include "align.h"
29#include "stack.h"
30#include "xcache_globals.h"
31#include "processor.h"
32#include "const_string.h"
33#include "opcode_spec.h"
34#include "utils.h"
35
36#define VAR_ENTRY_EXPIRED(pentry) ((pentry)->ttl && XG(request_time) > pentry->ctime + (pentry)->ttl)
37#define CHECK(x, e) do { if ((x) == NULL) { zend_error(E_ERROR, "XCache: " e); goto err; } } while (0)
38#define LOCK(x) xc_lock(x->lck)
39#define UNLOCK(x) xc_unlock(x->lck)
40
41#define ENTER_LOCK_EX(x) \
42    xc_lock(x->lck); \
43    zend_try { \
44        do
45#define LEAVE_LOCK_EX(x) \
46        while (0); \
47    } zend_catch { \
48        catched = 1; \
49    } zend_end_try(); \
50    xc_unlock(x->lck)
51
52#define ENTER_LOCK(x) do { \
53    int catched = 0; \
54    ENTER_LOCK_EX(x)
55#define LEAVE_LOCK(x) \
56    LEAVE_LOCK_EX(x); \
57    if (catched) { \
58        zend_bailout(); \
59    } \
60} while(0)
61
62/* }}} */
63
64/* {{{ globals */
65static char *xc_shm_scheme = NULL;
66static char *xc_mmap_path = NULL;
67static char *xc_coredump_dir = NULL;
68
69static xc_hash_t xc_php_hcache = {0};
70static xc_hash_t xc_php_hentry = {0};
71static xc_hash_t xc_var_hcache = {0};
72static xc_hash_t xc_var_hentry = {0};
73
74static zend_ulong xc_php_ttl    = 0;
75static zend_ulong xc_var_maxttl = 0;
76
77enum { xc_deletes_gc_interval = 120 };
78static zend_ulong xc_php_gc_interval = 0;
79static zend_ulong xc_var_gc_interval = 0;
80
81/* total size */
82static zend_ulong xc_php_size  = 0;
83static zend_ulong xc_var_size  = 0;
84
85static xc_cache_t **xc_php_caches = NULL;
86static xc_cache_t **xc_var_caches = NULL;
87
88static zend_bool xc_initized = 0;
89static zend_compile_file_t *origin_compile_file = NULL;
90static zend_compile_file_t *old_compile_file = NULL;
91static zend_llist_element  *xc_llist_zend_extension = NULL;
92
93static zend_bool xc_test = 0;
94static zend_bool xc_readonly_protection = 0;
95
96zend_bool xc_have_op_array_ctor = 0;
97
98static zend_bool xc_module_gotup = 0;
99static zend_bool xc_zend_extension_gotup = 0;
100static zend_bool xc_zend_extension_faked = 0;
101#if !COMPILE_DL_XCACHE
102#   define zend_extension_entry xcache_zend_extension_entry
103#endif
104ZEND_DLEXPORT zend_extension zend_extension_entry;
105ZEND_DECLARE_MODULE_GLOBALS(xcache);
106/* }}} */
107
108/* any function in *_dmz is only safe be called within locked(single thread) area */
109
110static inline int xc_entry_equal_dmz(xc_entry_t *a, xc_entry_t *b) /* {{{ */
111{
112    /* this function isn't required but can be in dmz */
113
114    if (a->type != b->type) {
115        return 0;
116    }
117    switch (a->type) {
118        case XC_TYPE_PHP:
119#ifdef HAVE_INODE
120            do {
121                xc_entry_data_php_t *ap = a->data.php;
122                xc_entry_data_php_t *bp = b->data.php;
123                if (ap->inode) {
124                    return ap->inode == bp->inode
125                        && ap->device == bp->device;
126                }
127            } while(0);
128#endif
129            /* fall */
130
131        case XC_TYPE_VAR:
132            do {
133#ifdef IS_UNICODE
134                if (a->name_type == IS_UNICODE) {
135                    if (a->name.ustr.len != b->name.ustr.len) {
136                        return 0;
137                    }
138                    return memcmp(a->name.ustr.val, b->name.ustr.val, (a->name.ustr.len + 1) * sizeof(UChar)) == 0;
139                }
140                else {
141                    return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
142                }
143#else
144                return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
145#endif
146
147            } while(0);
148        default:
149            assert(0);
150    }
151    return 0;
152}
153/* }}} */
154static void xc_entry_free_real_dmz(volatile xc_entry_t *xce) /* {{{ */
155{
156    xce->cache->mem->handlers->free(xce->cache->mem, (xc_entry_t *)xce);
157}
158/* }}} */
159static void xc_entry_add_dmz(xc_entry_t *xce) /* {{{ */
160{
161    xc_entry_t **head = &(xce->cache->entries[xce->hvalue]);
162    xce->next = *head;
163    *head = xce;
164    xce->cache->entries_count ++;
165}
166/* }}} */
167static xc_entry_t *xc_entry_store_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
168{
169    xc_entry_t *stored_xce;
170
171    xce->hits  = 0;
172    xce->ctime = XG(request_time);
173    xce->atime = XG(request_time);
174    stored_xce = xc_processor_store_xc_entry_t(xce TSRMLS_CC);
175    if (stored_xce) {
176        xc_entry_add_dmz(stored_xce);
177        return stored_xce;
178    }
179    else {
180        xce->cache->ooms ++;
181        return NULL;
182    }
183}
184/* }}} */
185static void xc_entry_free_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
186{
187    xce->cache->entries_count --;
188    if (xce->refcount == 0) {
189        xc_entry_free_real_dmz(xce);
190    }
191    else {
192        xce->next = xce->cache->deletes;
193        xce->cache->deletes = xce;
194        xce->dtime = XG(request_time);
195        xce->cache->deletes_count ++;
196    }
197    return;
198}
199/* }}} */
200static void xc_entry_remove_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
201{
202    xc_entry_t **pp = &(xce->cache->entries[xce->hvalue]);
203    xc_entry_t *p;
204    for (p = *pp; p; pp = &(p->next), p = p->next) {
205        if (xc_entry_equal_dmz(xce, p)) {
206            /* unlink */
207            *pp = p->next;
208            xc_entry_free_dmz(xce TSRMLS_CC);
209            return;
210        }
211    }
212    assert(0);
213}
214/* }}} */
215static xc_entry_t *xc_entry_find_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
216{
217    xc_entry_t *p;
218    for (p = xce->cache->entries[xce->hvalue]; p; p = p->next) {
219        if (xc_entry_equal_dmz(xce, p)) {
220            if (p->type == XC_TYPE_VAR || /* PHP */ p->data.php->mtime == xce->data.php->mtime) {
221                p->hits ++;
222                p->atime = XG(request_time);
223                return p;
224            }
225            else {
226                xc_entry_remove_dmz(p TSRMLS_CC);
227                return NULL;
228            }
229        }
230    }
231    return NULL;
232}
233/* }}} */
234static void xc_entry_hold_php_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
235{
236    TRACE("hold %s", xce->name.str.val);
237    xce->refcount ++;
238    xc_stack_push(&XG(php_holds)[xce->cache->cacheid], (void *)xce);
239}
240/* }}} */
241#if 0
242static void xc_entry_hold_var_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
243{
244    xce->refcount ++;
245    xc_stack_push(&XG(var_holds)[xce->cache->cacheid], (void *)xce);
246}
247/* }}} */
248#endif
249
250/* helper function that loop through each entry */
251#define XC_ENTRY_APPLY_FUNC(name) int name(xc_entry_t *entry TSRMLS_DC)
252typedef XC_ENTRY_APPLY_FUNC((*cache_apply_dmz_func_t));
253static void xc_entry_apply_dmz(xc_cache_t *cache, cache_apply_dmz_func_t apply_func TSRMLS_DC) /* {{{ */
254{
255    xc_entry_t *p, **pp;
256    int i, c;
257
258    for (i = 0, c = cache->hentry->size; i < c; i ++) {
259        pp = &(cache->entries[i]);
260        for (p = *pp; p; p = *pp) {
261            if (apply_func(p TSRMLS_CC)) {
262                /* unlink */
263                *pp = p->next;
264                xc_entry_free_dmz(p TSRMLS_CC);
265            }
266            else {
267                pp = &(p->next);
268            }
269        }
270    }
271}
272/* }}} */
273
274#define XC_CACHE_APPLY_FUNC(name) void name(xc_cache_t *cache TSRMLS_DC)
275/* call graph:
276 * xc_gc_expires_php -> xc_gc_expires_one -> xc_entry_apply_dmz -> xc_gc_expires_php_entry_dmz
277 * xc_gc_expires_var -> xc_gc_expires_one -> xc_entry_apply_dmz -> xc_gc_expires_var_entry_dmz
278 */
279static XC_ENTRY_APPLY_FUNC(xc_gc_expires_php_entry_dmz) /* {{{ */
280{
281    TRACE("ttl %lu, %lu %lu", (zend_ulong) XG(request_time), (zend_ulong) entry->atime, xc_php_ttl);
282    if (XG(request_time) > entry->atime + xc_php_ttl) {
283        return 1;
284    }
285    return 0;
286}
287/* }}} */
288static XC_ENTRY_APPLY_FUNC(xc_gc_expires_var_entry_dmz) /* {{{ */
289{
290    if (VAR_ENTRY_EXPIRED(entry)) {
291        return 1;
292    }
293    return 0;
294}
295/* }}} */
296static void xc_gc_expires_one(xc_cache_t *cache, zend_ulong gc_interval, cache_apply_dmz_func_t apply_func TSRMLS_DC) /* {{{ */
297{
298    TRACE("interval %lu, %lu %lu", (zend_ulong) XG(request_time), (zend_ulong) cache->last_gc_expires, gc_interval);
299    if (XG(request_time) - cache->last_gc_expires >= gc_interval) {
300        ENTER_LOCK(cache) {
301            if (XG(request_time) - cache->last_gc_expires >= gc_interval) {
302                cache->last_gc_expires = XG(request_time);
303                xc_entry_apply_dmz(cache, apply_func TSRMLS_CC);
304            }
305        } LEAVE_LOCK(cache);
306    }
307}
308/* }}} */
309static void xc_gc_expires_php(TSRMLS_D) /* {{{ */
310{
311    int i, c;
312
313    if (!xc_php_ttl || !xc_php_gc_interval) {
314        return;
315    }
316
317    for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
318        xc_gc_expires_one(xc_php_caches[i], xc_php_gc_interval, xc_gc_expires_php_entry_dmz TSRMLS_CC);
319    }
320}
321/* }}} */
322static void xc_gc_expires_var(TSRMLS_D) /* {{{ */
323{
324    int i, c;
325
326    if (!xc_var_gc_interval) {
327        return;
328    }
329
330    for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
331        xc_gc_expires_one(xc_var_caches[i], xc_var_gc_interval, xc_gc_expires_var_entry_dmz TSRMLS_CC);
332    }
333}
334/* }}} */
335
336static XC_CACHE_APPLY_FUNC(xc_gc_delete_dmz) /* {{{ */
337{
338    xc_entry_t *p, **pp;
339
340    pp = &cache->deletes;
341    for (p = *pp; p; p = *pp) {
342        if (XG(request_time) - p->dtime > 3600) {
343            p->refcount = 0;
344            /* issue warning here */
345        }
346        if (p->refcount == 0) {
347            /* unlink */
348            *pp = p->next;
349            cache->deletes_count --;
350            xc_entry_free_real_dmz(p);
351        }
352        else {
353            pp = &(p->next);
354        }
355    }
356}
357/* }}} */
358static XC_CACHE_APPLY_FUNC(xc_gc_deletes_one) /* {{{ */
359{
360    if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
361        ENTER_LOCK(cache) {
362            if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
363                cache->last_gc_deletes = XG(request_time);
364                xc_gc_delete_dmz(cache TSRMLS_CC);
365            }
366        } LEAVE_LOCK(cache);
367    }
368}
369/* }}} */
370static void xc_gc_deletes(TSRMLS_D) /* {{{ */
371{
372    int i, c;
373
374    for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
375        xc_gc_deletes_one(xc_php_caches[i] TSRMLS_CC);
376    }
377
378    for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
379        xc_gc_deletes_one(xc_var_caches[i] TSRMLS_CC);
380    }
381}
382/* }}} */
383
384/* helper functions for user functions */
385static void xc_fillinfo_dmz(int cachetype, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
386{
387    zval *blocks;
388    const xc_block_t *b;
389#ifndef NDEBUG
390    xc_memsize_t avail = 0;
391#endif
392    xc_mem_t *mem = cache->mem;
393    const xc_mem_handlers_t *handlers = mem->handlers;
394    zend_ulong interval;
395    if (cachetype == XC_TYPE_PHP) {
396        interval = xc_php_ttl ? xc_php_gc_interval : 0;
397    }
398    else {
399        interval = xc_var_gc_interval;
400    }
401
402    add_assoc_long_ex(return_value, ZEND_STRS("slots"),     cache->hentry->size);
403    add_assoc_long_ex(return_value, ZEND_STRS("compiling"), cache->compiling);
404    add_assoc_long_ex(return_value, ZEND_STRS("misses"),    cache->misses);
405    add_assoc_long_ex(return_value, ZEND_STRS("hits"),      cache->hits);
406    add_assoc_long_ex(return_value, ZEND_STRS("clogs"),     cache->clogs);
407    add_assoc_long_ex(return_value, ZEND_STRS("ooms"),      cache->ooms);
408    add_assoc_long_ex(return_value, ZEND_STRS("errors"),    cache->errors);
409
410    add_assoc_long_ex(return_value, ZEND_STRS("cached"),    cache->entries_count);
411    add_assoc_long_ex(return_value, ZEND_STRS("deleted"),   cache->deletes_count);
412    if (interval) {
413        time_t gc = (cache->last_gc_expires + interval) - XG(request_time);
414        add_assoc_long_ex(return_value, ZEND_STRS("gc"),    gc > 0 ? gc : 0);
415    }
416    else {
417        add_assoc_null_ex(return_value, ZEND_STRS("gc"));
418    }
419
420    MAKE_STD_ZVAL(blocks);
421    array_init(blocks);
422
423    add_assoc_long_ex(return_value, ZEND_STRS("size"),  handlers->size(mem));
424    add_assoc_long_ex(return_value, ZEND_STRS("avail"), handlers->avail(mem));
425    add_assoc_bool_ex(return_value, ZEND_STRS("can_readonly"), xc_readonly_protection);
426
427    for (b = handlers->freeblock_first(mem); b; b = handlers->freeblock_next(b)) {
428        zval *bi;
429
430        MAKE_STD_ZVAL(bi);
431        array_init(bi);
432
433        add_assoc_long_ex(bi, ZEND_STRS("size"),   handlers->block_size(b));
434        add_assoc_long_ex(bi, ZEND_STRS("offset"), handlers->block_offset(mem, b));
435        add_next_index_zval(blocks, bi);
436#ifndef NDEBUG
437        avail += handlers->block_size(b);
438#endif
439    }
440    add_assoc_zval_ex(return_value, ZEND_STRS("free_blocks"), blocks);
441#ifndef NDEBUG
442    assert(avail == handlers->avail(mem));
443#endif
444}
445/* }}} */
446static void xc_fillentry_dmz(xc_entry_t *entry, int del, zval *list TSRMLS_DC) /* {{{ */
447{
448    zval* ei;
449    xc_entry_data_php_t *php;
450    xc_entry_data_var_t *var;
451
452    ALLOC_INIT_ZVAL(ei);
453    array_init(ei);
454
455    add_assoc_long_ex(ei, ZEND_STRS("size"),     entry->size);
456    add_assoc_long_ex(ei, ZEND_STRS("refcount"), entry->refcount);
457    add_assoc_long_ex(ei, ZEND_STRS("hits"),     entry->hits);
458    add_assoc_long_ex(ei, ZEND_STRS("ctime"),    entry->ctime);
459    add_assoc_long_ex(ei, ZEND_STRS("atime"),    entry->atime);
460    add_assoc_long_ex(ei, ZEND_STRS("hvalue"),   entry->hvalue);
461    if (del) {
462        add_assoc_long_ex(ei, ZEND_STRS("dtime"), entry->dtime);
463    }
464#ifdef IS_UNICODE
465    do {
466        zval *zv;
467        ALLOC_INIT_ZVAL(zv);
468        switch (entry->name_type) {
469            case IS_UNICODE:
470                    ZVAL_UNICODEL(zv, entry->name.ustr.val, entry->name.ustr.len, 1);
471                break;
472            case IS_STRING:
473                ZVAL_STRINGL(zv, entry->name.str.val, entry->name.str.len, 1);
474                break;
475            default:
476                assert(0);
477        }
478        zv->type = entry->name_type;
479        add_assoc_zval_ex(ei, ZEND_STRS("name"), zv);
480    } while (0);
481#else
482    add_assoc_stringl_ex(ei, ZEND_STRS("name"), entry->name.str.val, entry->name.str.len, 1);
483#endif
484    switch (entry->type) {
485        case XC_TYPE_PHP:
486            php = entry->data.php;
487            add_assoc_long_ex(ei, ZEND_STRS("sourcesize"),    php->sourcesize);
488#ifdef HAVE_INODE
489            add_assoc_long_ex(ei, ZEND_STRS("device"),        php->device);
490            add_assoc_long_ex(ei, ZEND_STRS("inode"),         php->inode);
491#endif
492            add_assoc_long_ex(ei, ZEND_STRS("mtime"),         php->mtime);
493
494#ifdef HAVE_XCACHE_CONSTANT
495            add_assoc_long_ex(ei, ZEND_STRS("constinfo_cnt"), php->constinfo_cnt);
496#endif
497            add_assoc_long_ex(ei, ZEND_STRS("function_cnt"),  php->funcinfo_cnt);
498            add_assoc_long_ex(ei, ZEND_STRS("class_cnt"),     php->classinfo_cnt);
499#ifdef ZEND_ENGINE_2_1
500            add_assoc_long_ex(ei, ZEND_STRS("autoglobal_cnt"),php->autoglobal_cnt);
501#endif
502            break;
503        case XC_TYPE_VAR:
504            var = entry->data.var;
505            break;
506
507        default:
508            assert(0);
509    }
510
511    add_next_index_zval(list, ei);
512}
513/* }}} */
514static void xc_filllist_dmz(xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
515{
516    zval* list;
517    int i, c;
518    xc_entry_t *e;
519
520    ALLOC_INIT_ZVAL(list);
521    array_init(list);
522
523    for (i = 0, c = cache->hentry->size; i < c; i ++) {
524        for (e = cache->entries[i]; e; e = e->next) {
525            xc_fillentry_dmz(e, 0, list TSRMLS_CC);
526        }
527    }
528    add_assoc_zval(return_value, "cache_list", list);
529
530    ALLOC_INIT_ZVAL(list);
531    array_init(list);
532    for (e = cache->deletes; e; e = e->next) {
533        xc_fillentry_dmz(e, 1, list TSRMLS_CC);
534    }
535    add_assoc_zval(return_value, "deleted_list", list);
536}
537/* }}} */
538
539static zend_op_array *xc_entry_install(xc_entry_t *xce, zend_file_handle *h TSRMLS_DC) /* {{{ */
540{
541    zend_uint i;
542    xc_entry_data_php_t *p = xce->data.php;
543    zend_op_array *old_active_op_array = CG(active_op_array);
544    ALLOCA_FLAG(use_heap)
545
546#ifndef ZEND_ENGINE_2
547    /* new ptr which is stored inside CG(class_table) */
548    xc_cest_t **new_cest_ptrs = (xc_cest_t **)my_do_alloca(sizeof(xc_cest_t*) * p->classinfo_cnt, use_heap);
549#endif
550
551    CG(active_op_array) = p->op_array;
552
553#ifdef HAVE_XCACHE_CONSTANT
554    /* install constant */
555    for (i = 0; i < p->constinfo_cnt; i ++) {
556        xc_constinfo_t *ci = &p->constinfos[i];
557        xc_install_constant(xce->name.str.val, &ci->constant,
558                UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
559    }
560#endif
561
562    /* install function */
563    for (i = 0; i < p->funcinfo_cnt; i ++) {
564        xc_funcinfo_t  *fi = &p->funcinfos[i];
565        xc_install_function(xce->name.str.val, &fi->func,
566                UNISW(0, fi->type), fi->key, fi->key_size, fi->h TSRMLS_CC);
567    }
568
569    /* install class */
570    for (i = 0; i < p->classinfo_cnt; i ++) {
571        xc_classinfo_t *ci = &p->classinfos[i];
572#ifndef ZEND_ENGINE_2
573        zend_class_entry *ce = CestToCePtr(ci->cest);
574        /* fix pointer to the be which inside class_table */
575        if (ce->parent) {
576            zend_uint class_idx = (/* class_num */ (int) (long) ce->parent) - 1;
577            assert(class_idx < i);
578            ci->cest.parent = new_cest_ptrs[class_idx];
579        }
580        new_cest_ptrs[i] =
581#endif
582        xc_install_class(xce->name.str.val, &ci->cest, ci->oplineno,
583                UNISW(0, ci->type), ci->key, ci->key_size, ci->h TSRMLS_CC);
584    }
585
586#ifdef ZEND_ENGINE_2_1
587    /* trigger auto_globals jit */
588    for (i = 0; i < p->autoglobal_cnt; i ++) {
589        xc_autoglobal_t *aginfo = &p->autoglobals[i];
590        /*
591        zend_auto_global *auto_global;
592        if (zend_u_hash_quick_find(CG(auto_globals), aginfo->type, aginfo->key, aginfo->key_len+1, aginfo->h, (void **) &auto_global)==SUCCESS) {
593            if (auto_global->armed) {
594                auto_global->armed = auto_global->auto_global_callback(auto_global->name, auto_global->name_len TSRMLS_CC);
595            }
596        }
597        */
598        zend_u_is_auto_global(aginfo->type, aginfo->key, aginfo->key_len TSRMLS_CC);
599    }
600#endif
601
602    i = 1;
603    zend_hash_add(&EG(included_files), xce->name.str.val, xce->name.str.len+1, (void *)&i, sizeof(int), NULL);
604    if (h) {
605        zend_llist_add_element(&CG(open_files), h);
606    }
607
608#ifndef ZEND_ENGINE_2
609    my_free_alloca(new_cest_ptrs, use_heap);
610#endif
611    CG(active_op_array) = old_active_op_array;
612    return p->op_array;
613}
614/* }}} */
615
616static inline void xc_entry_unholds_real(xc_stack_t *holds, xc_cache_t **caches, int cachecount TSRMLS_DC) /* {{{ */
617{
618    int i;
619    xc_stack_t *s;
620    xc_cache_t *cache;
621    xc_entry_t *xce;
622
623    for (i = 0; i < cachecount; i ++) {
624        s = &holds[i];
625        TRACE("holded %d", xc_stack_count(s));
626        if (xc_stack_count(s)) {
627            cache = ((xc_entry_t *)xc_stack_top(s))->cache;
628            ENTER_LOCK(cache) {
629                while (xc_stack_count(s)) {
630                    xce = (xc_entry_t*) xc_stack_pop(s);
631                    TRACE("unhold %s", xce->name.str.val);
632                    xce->refcount --;
633                    assert(xce->refcount >= 0);
634                }
635            } LEAVE_LOCK(cache);
636        }
637    }
638}
639/* }}} */
640static void xc_entry_unholds(TSRMLS_D) /* {{{ */
641{
642    xc_entry_unholds_real(XG(php_holds), xc_php_caches, xc_php_hcache.size TSRMLS_CC);
643    xc_entry_unholds_real(XG(var_holds), xc_var_caches, xc_var_hcache.size TSRMLS_CC);
644}
645/* }}} */
646static int xc_stat(const char *filename, const char *include_path, struct stat *pbuf TSRMLS_DC) /* {{{ */
647{
648    char filepath[MAXPATHLEN];
649    char *paths, *path;
650    char *tokbuf;
651    int size = strlen(include_path) + 1;
652    char tokens[] = { DEFAULT_DIR_SEPARATOR, '\0' };
653    ALLOCA_FLAG(use_heap)
654
655    paths = (char *)my_do_alloca(size, use_heap);
656    memcpy(paths, include_path, size);
657
658    for (path = php_strtok_r(paths, tokens, &tokbuf); path; path = php_strtok_r(NULL, tokens, &tokbuf)) {
659        if (snprintf(filepath, sizeof(filepath), "%s/%s", path, filename) >= MAXPATHLEN - 1) {
660            continue;
661        }
662        if (VCWD_STAT(filepath, pbuf) == 0) {
663            my_free_alloca(paths, use_heap);
664            return SUCCESS;
665        }
666    }
667
668    my_free_alloca(paths, use_heap);
669
670    return FAILURE;
671}
672/* }}} */
673
674#define HASH(i) (i)
675#define HASH_USTR_L(t, s, l) HASH(zend_u_inline_hash_func(t, s, (l + 1) * sizeof(UChar)))
676#define HASH_STR_L(s, l) HASH(zend_inline_hash_func(s, l + 1))
677#define HASH_STR(s) HASH_STR_L(s, strlen(s) + 1)
678#define HASH_NUM(n) HASH(n)
679static inline xc_hash_value_t xc_hash_fold(xc_hash_value_t hvalue, const xc_hash_t *hasher) /* {{{ fold hash bits as needed */
680{
681    xc_hash_value_t folded = 0;
682    while (hvalue) {
683        folded ^= (hvalue & hasher->mask);
684        hvalue >>= hasher->bits;
685    }
686    return folded;
687}
688/* }}} */
689static inline xc_hash_value_t xc_entry_hash_name(xc_entry_t *xce TSRMLS_DC) /* {{{ */
690{
691    return UNISW(NOTHING, UG(unicode) ? HASH_USTR_L(xce->name_type, xce->name.uni.val, xce->name.uni.len) :)
692        HASH_STR_L(xce->name.str.val, xce->name.str.len);
693}
694/* }}} */
695#define xc_entry_hash_var xc_entry_hash_name
696static inline xc_hash_value_t xc_entry_hash_php(xc_entry_t *xce TSRMLS_DC) /* {{{ */
697{
698#ifdef HAVE_INODE
699    if (xce->data.php->inode) {
700        return HASH(xce->data.php->device + xce->data.php->inode);
701    }
702#endif
703    return xc_entry_hash_name(xce TSRMLS_CC);
704}
705/* }}} */
706static int xc_entry_init_key_php(xc_entry_t *xce, char *filename, char *opened_path_buffer TSRMLS_DC) /* {{{ */
707{
708    struct stat buf, *pbuf;
709    xc_hash_value_t hv;
710    int cacheid;
711    xc_entry_data_php_t *php;
712    char *ptr;
713    time_t delta;
714
715    if (!filename || !SG(request_info).path_translated) {
716        return FAILURE;
717    }
718
719    if (strstr(filename, "://") != NULL) {
720        return FAILURE;
721    }
722
723    php = xce->data.php;
724
725    if (XG(stat)) {
726        if (strcmp(SG(request_info).path_translated, filename) == 0) {
727            /* sapi has already done this stat() for us */
728            pbuf = sapi_get_stat(TSRMLS_C);
729            if (pbuf) {
730                goto stat_done;
731            }
732        }
733
734        /* absolute path */
735        pbuf = &buf;
736        if (IS_ABSOLUTE_PATH(filename, strlen(filename))) {
737            if (VCWD_STAT(filename, pbuf) != 0) {
738                return FAILURE;
739            }
740            goto stat_done;
741        }
742
743        /* relative path */
744        if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) {
745            ptr = filename + 1;
746            if (*ptr == '.') {
747                while (*(++ptr) == '.');
748                if (!IS_SLASH(*ptr)) {
749                    goto not_relative_path;
750                }   
751            }
752
753            if (VCWD_STAT(filename, pbuf) != 0) {
754                return FAILURE;
755            }
756            goto stat_done;
757        }
758not_relative_path:
759
760        /* use include_path */
761        if (xc_stat(filename, PG(include_path), pbuf TSRMLS_CC) != SUCCESS) {
762            return FAILURE;
763        }
764
765        /* fall */
766
767stat_done:
768        delta = XG(request_time) - pbuf->st_mtime;
769        if (abs(delta) < 2 && !xc_test) {
770            return FAILURE;
771        }
772
773        php->mtime        = pbuf->st_mtime;
774#ifdef HAVE_INODE
775        php->device       = pbuf->st_dev;
776        php->inode        = pbuf->st_ino;
777#endif
778        php->sourcesize   = pbuf->st_size;
779    }
780    else { /* XG(inode) */
781        php->mtime        = 0;
782#ifdef HAVE_INODE
783        php->device       = 0;
784        php->inode        = 0;
785#endif
786        php->sourcesize   = 0;
787    }
788
789#ifdef HAVE_INODE
790    if (!php->inode)
791#endif
792    {
793        /* hash on filename, let's expand it to real path */
794        filename = expand_filepath(filename, opened_path_buffer TSRMLS_CC);
795        if (filename == NULL) {
796            return FAILURE;
797        }
798    }
799
800    UNISW(NOTHING, xce->name_type = IS_STRING;)
801    xce->name.str.val = filename;
802    xce->name.str.len = strlen(filename);
803
804    hv = xc_entry_hash_php(xce TSRMLS_CC);
805    cacheid = xc_hash_fold(hv, &xc_php_hcache);
806    xce->cache = xc_php_caches[cacheid];
807    xce->hvalue = xc_hash_fold(hv, &xc_php_hentry);
808
809    xce->type = XC_TYPE_PHP;
810    return SUCCESS;
811}
812/* }}} */
813static inline xc_hash_value_t xc_php_hash_md5(xc_entry_data_php_t *php TSRMLS_DC) /* {{{ */
814{
815    return HASH_STR_S(php->md5, sizeof(php->md5));
816}
817/* }}} */
818static void xc_cache_early_binding_class_cb(zend_op *opline, int oplineno, void *data TSRMLS_DC) /* {{{ */
819{
820    char *class_name;
821    int i, class_len;
822    xc_cest_t cest;
823    xc_entry_data_php_t *php = (xc_entry_data_php_t *) data;
824
825    class_name = opline->op1.u.constant.value.str.val;
826    class_len  = opline->op1.u.constant.value.str.len;
827    if (zend_hash_find(CG(class_table), class_name, class_len, (void **) &cest) == FAILURE) {
828        assert(0);
829    }
830    TRACE("got ZEND_DECLARE_INHERITED_CLASS: %s", class_name + 1);
831    /* let's see which class */
832    for (i = 0; i < php->classinfo_cnt; i ++) {
833        if (memcmp(ZSTR_S(php->classinfos[i].key), class_name, class_len) == 0) {
834            php->classinfos[i].oplineno = oplineno;
835            php->have_early_binding = 1;
836            break;
837        }
838    }
839
840    if (i == php->classinfo_cnt) {
841        assert(0);
842    }
843}
844/* }}} */
845static zend_op_array *xc_check_initial_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
846{
847    XG(initial_compile_file_called) = 1;
848    return origin_compile_file(h, type TSRMLS_CC);
849}
850/* }}} */
851static zend_op_array *xc_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
852{
853    xc_sandbox_t sandbox;
854    zend_op_array *op_array;
855    xc_entry_t xce, *stored_xce;
856    xc_entry_data_php_t php;
857    xc_cache_t *cache;
858    zend_bool clogged = 0;
859    zend_bool catched = 0;
860    char *filename;
861    char opened_path_buffer[MAXPATHLEN];
862    int old_constinfo_cnt, old_funcinfo_cnt, old_classinfo_cnt;
863    int i;
864
865    if (!xc_initized) {
866        assert(0);
867    }
868
869    if (!XG(cacher)) {
870        op_array = old_compile_file(h, type TSRMLS_CC);
871#ifdef HAVE_XCACHE_OPTIMIZER
872        if (XG(optimizer)) {
873            xc_optimize(op_array TSRMLS_CC);
874        }
875#endif
876        return op_array;
877    }
878
879    /* {{{ prepare key
880     * include_once() and require_once() gives us opened_path
881     * however, include() and require() non-absolute path which break
882     * included_files, and may confuse with (include|require)_once
883     * -- Xuefer
884     */
885
886    filename = h->opened_path ? h->opened_path : h->filename;
887    xce.data.php = &php;
888    if (xc_entry_init_key_php(&xce, filename, opened_path_buffer TSRMLS_CC) != SUCCESS) {
889        return old_compile_file(h, type TSRMLS_CC);
890    }
891    cache = xce.cache;
892    /* }}} */
893    /* {{{ restore */
894    /* stale precheck */
895    if (cache->compiling) {
896        cache->clogs ++; /* is it safe here? */
897        return old_compile_file(h, type TSRMLS_CC);
898    }
899
900    stored_xce = NULL;
901    op_array = NULL;
902    ENTER_LOCK_EX(cache) {
903        /* clogged */
904        if (cache->compiling) {
905            cache->clogs ++;
906            op_array = NULL;
907            clogged = 1;
908            break;
909        }
910
911        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
912        /* found */
913        if (stored_xce) {
914            TRACE("found %s, catch it", stored_xce->name.str.val);
915            xc_entry_hold_php_dmz(stored_xce TSRMLS_CC);
916            cache->hits ++;
917            break;
918        }
919
920        cache->compiling = XG(request_time);
921        cache->misses ++;
922    } LEAVE_LOCK_EX(cache);
923
924    if (catched) {
925        cache->compiling = 0;
926        zend_bailout();
927    }
928
929    /* found */
930    if (stored_xce) {
931        goto restore;
932    }
933
934    /* clogged */
935    if (clogged) {
936        return old_compile_file(h, type TSRMLS_CC);
937    }
938    /* }}} */
939
940    /* {{{ compile */
941    TRACE("compiling %s", filename);
942
943    /* make compile inside sandbox */
944    xc_sandbox_init(&sandbox, filename TSRMLS_CC);
945
946    old_classinfo_cnt = zend_hash_num_elements(CG(class_table));
947    old_funcinfo_cnt  = zend_hash_num_elements(CG(function_table));
948    old_constinfo_cnt  = zend_hash_num_elements(EG(zend_constants));
949
950    XG(initial_compile_file_called) = 0;
951    zend_try {
952        op_array = old_compile_file(h, type TSRMLS_CC);
953    } zend_catch {
954        catched = 1;
955    } zend_end_try();
956
957    if (catched) {
958        goto err_bailout;
959    }
960
961    if (op_array == NULL) {
962        goto err_oparray;
963    }
964
965    if (!XG(initial_compile_file_called)) {
966        xc_sandbox_free(&sandbox, XC_InstallNoBinding TSRMLS_CC);
967        ENTER_LOCK(cache) {
968            cache->compiling = 0;
969            /* it's not cachable, but don't scare the users with high misses */
970            cache->misses --;
971        } LEAVE_LOCK(cache);
972        return op_array;
973    }
974
975    filename = h->opened_path ? h->opened_path : h->filename;
976    /* none-inode enabled entry hash/compare on name
977     * do not update to its name to real pathname
978     */
979#ifdef HAVE_INODE
980    if (xce.data.php->inode)
981    {
982        if (xce.name.str.val != filename) {
983            xce.name.str.val = filename;
984            xce.name.str.len = strlen(filename);
985        }
986    }
987#endif
988
989#ifdef HAVE_XCACHE_OPTIMIZER
990    if (XG(optimizer)) {
991        xc_optimize(op_array TSRMLS_CC);
992    }
993#endif
994    /* }}} */
995    /* {{{ prepare */
996    php.op_array      = op_array;
997
998#ifdef HAVE_XCACHE_CONSTANT
999    php.constinfo_cnt  = zend_hash_num_elements(EG(zend_constants)) - old_constinfo_cnt;
1000#endif
1001    php.funcinfo_cnt   = zend_hash_num_elements(CG(function_table)) - old_funcinfo_cnt;
1002    php.classinfo_cnt  = zend_hash_num_elements(CG(class_table))    - old_classinfo_cnt;
1003#ifdef ZEND_ENGINE_2_1
1004    /* {{{ count php.autoglobal_cnt */ {
1005        Bucket *b;
1006
1007        php.autoglobal_cnt = 0;
1008        for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
1009            zend_auto_global *auto_global = (zend_auto_global *) b->pData;
1010            /* check if actived */
1011            if (auto_global->auto_global_callback && !auto_global->armed) {
1012                php.autoglobal_cnt ++;
1013            }
1014        }
1015    }
1016    /* }}} */
1017#endif
1018
1019#define X_ALLOC_N(var, cnt) do {     \
1020    if (php.cnt) {                   \
1021        ECALLOC_N(php.var, php.cnt); \
1022        if (!php.var) {              \
1023            goto err_##var;          \
1024        }                            \
1025    }                                \
1026    else {                           \
1027        php.var = NULL;              \
1028    }                                \
1029} while (0)
1030
1031#ifdef HAVE_XCACHE_CONSTANT
1032    X_ALLOC_N(constinfos,  constinfo_cnt);
1033#endif
1034    X_ALLOC_N(funcinfos,   funcinfo_cnt);
1035    X_ALLOC_N(classinfos,  classinfo_cnt);
1036#ifdef ZEND_ENGINE_2_1
1037    X_ALLOC_N(autoglobals, autoglobal_cnt);
1038#endif
1039#undef X_ALLOC
1040    /* }}} */
1041    /* {{{ shallow copy, pointers only */ {
1042        Bucket *b;
1043        unsigned int i;
1044        unsigned int j;
1045
1046#define COPY_H(vartype, var, cnt, name, datatype) do {        \
1047    for (i = 0, j = 0; b; i ++, b = b->pListNext) {           \
1048        vartype *data = &php.var[j];                          \
1049                                                              \
1050        if (i < old_##cnt) {                                  \
1051            continue;                                         \
1052        }                                                     \
1053        j ++;                                                 \
1054                                                              \
1055        assert(i < old_##cnt + php.cnt);                      \
1056        assert(b->pData);                                     \
1057        memcpy(&data->name, b->pData, sizeof(datatype));      \
1058        UNISW(NOTHING, data->type = b->key.type;)             \
1059        if (UNISW(1, b->key.type == IS_STRING)) {             \
1060            ZSTR_S(data->key)      = BUCKET_KEY_S(b);          \
1061        }                                                     \
1062        else {                                                \
1063            ZSTR_U(data->key)      = BUCKET_KEY_U(b);         \
1064        }                                                     \
1065        data->key_size   = b->nKeyLength;                     \
1066        data->h          = b->h;                              \
1067    }                                                         \
1068} while(0)
1069
1070#ifdef HAVE_XCACHE_CONSTANT
1071        b = EG(zend_constants)->pListHead; COPY_H(xc_constinfo_t, constinfos, constinfo_cnt, constant, zend_constant);
1072#endif
1073        b = CG(function_table)->pListHead; COPY_H(xc_funcinfo_t,  funcinfos,  funcinfo_cnt,  func,     zend_function);
1074        b = CG(class_table)->pListHead;    COPY_H(xc_classinfo_t, classinfos, classinfo_cnt, cest,     xc_cest_t);
1075
1076#undef COPY_H
1077
1078        /* for ZE1, cest need to be fixed inside store */
1079
1080#ifdef ZEND_ENGINE_2_1
1081        /* scan for acatived auto globals */
1082        i = 0;
1083        for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
1084            zend_auto_global *auto_global = (zend_auto_global *) b->pData;
1085            /* check if actived */
1086            if (auto_global->auto_global_callback && !auto_global->armed) {
1087                xc_autoglobal_t *data = &php.autoglobals[i];
1088
1089                assert(i < php.autoglobal_cnt);
1090                i ++;
1091                UNISW(NOTHING, data->type = b->key.type;)
1092                if (UNISW(1, b->key.type == IS_STRING)) {
1093                    ZSTR_S(data->key)     = BUCKET_KEY_S(b);
1094                }
1095                else {
1096                    ZSTR_U(data->key)     = BUCKET_KEY_U(b);
1097                }
1098                data->key_len = b->nKeyLength - 1;
1099                data->h       = b->h;
1100            }
1101        }
1102#endif
1103    }
1104    /* }}} */
1105    /* {{{ find inherited classes that should be early-binding */
1106    php.have_early_binding = 0;
1107    for (i = 0; i < php.classinfo_cnt; i ++) {
1108        php.classinfos[i].oplineno = -1;
1109    }
1110
1111    xc_undo_pass_two(php.op_array TSRMLS_CC);
1112    xc_foreach_early_binding_class(php.op_array, xc_cache_early_binding_class_cb, (void *) &php TSRMLS_CC);
1113    xc_redo_pass_two(php.op_array TSRMLS_CC);
1114    /* }}} */
1115#ifdef SHOW_DPRINT
1116    xc_dprint(&xce, 0 TSRMLS_CC);
1117#endif
1118    ENTER_LOCK_EX(cache) { /* {{{ store/add entry */
1119        stored_xce = xc_entry_store_dmz(&xce TSRMLS_CC);
1120    } LEAVE_LOCK_EX(cache);
1121    /* }}} */
1122    TRACE("%s", "stored");
1123
1124#define X_FREE(var) \
1125    if (xce.data.php->var) { \
1126        efree(xce.data.php->var); \
1127    } \
1128err_##var:
1129
1130#ifdef ZEND_ENGINE_2_1
1131    X_FREE(autoglobals)
1132#endif
1133    X_FREE(classinfos)
1134    X_FREE(funcinfos)
1135#ifdef HAVE_XCACHE_CONSTANT
1136    X_FREE(constinfos)
1137#endif
1138#undef X_FREE
1139
1140err_oparray:
1141err_bailout:
1142
1143    if (xc_test && stored_xce) {
1144        /* free it, no install. restore now */
1145        xc_sandbox_free(&sandbox, XC_NoInstall TSRMLS_CC);
1146    }
1147    else if (!op_array) {
1148        /* failed to compile free it, no install */
1149        xc_sandbox_free(&sandbox, XC_NoInstall TSRMLS_CC);
1150    }
1151    else {
1152        zend_op_array *old_active_op_array = CG(active_op_array);
1153        CG(active_op_array) = op_array;
1154        xc_sandbox_free(&sandbox, XC_Install TSRMLS_CC);
1155        CG(active_op_array) = old_active_op_array;
1156    }
1157
1158    ENTER_LOCK(cache) {
1159        cache->compiling = 0;
1160    } LEAVE_LOCK(cache);
1161    if (catched) {
1162        cache->errors ++;
1163        zend_bailout();
1164    }
1165    if (xc_test && stored_xce) {
1166#ifdef ZEND_ENGINE_2
1167        destroy_op_array(op_array TSRMLS_CC);
1168#else
1169        destroy_op_array(op_array);
1170#endif
1171        efree(op_array);
1172        h = NULL;
1173        goto restore;
1174    }
1175    return op_array;
1176
1177restore:
1178    CG(in_compilation)    = 1;
1179    CG(compiled_filename) = stored_xce->name.str.val;
1180    CG(zend_lineno)       = 0;
1181    TRACE("restoring %s", stored_xce->name.str.val);
1182    xc_processor_restore_xc_entry_t(&xce, stored_xce, xc_readonly_protection TSRMLS_CC);
1183#ifdef SHOW_DPRINT
1184    xc_dprint(&xce, 0 TSRMLS_CC);
1185#endif
1186
1187    catched = 0;
1188    zend_try {
1189        op_array = xc_entry_install(&xce, h TSRMLS_CC);
1190    } zend_catch {
1191        catched = 1;
1192    } zend_end_try();
1193
1194#define X_FREE(var) \
1195    if (xce.data.php->var) { \
1196        efree(xce.data.php->var); \
1197    }
1198#ifdef ZEND_ENGINE_2_1
1199    X_FREE(autoglobals)
1200#endif
1201    X_FREE(classinfos)
1202    X_FREE(funcinfos)
1203#ifdef HAVE_XCACHE_CONSTANT
1204    X_FREE(constinfos)
1205#endif
1206#undef X_FREE
1207    efree(xce.data.php);
1208
1209    if (catched) {
1210        zend_bailout();
1211    }
1212    CG(in_compilation)    = 0;
1213    CG(compiled_filename) = NULL;
1214    TRACE("restored  %s", stored_xce->name.str.val);
1215    return op_array;
1216}
1217/* }}} */
1218
1219/* gdb helper functions, but N/A for coredump */
1220int xc_is_rw(const void *p) /* {{{ */
1221{
1222    xc_shm_t *shm;
1223    int i;
1224    if (!xc_initized) {
1225        return 0;
1226    }
1227    for (i = 0; i < xc_php_hcache.size; i ++) {
1228        shm = xc_php_caches[i]->shm;
1229        if (shm->handlers->is_readwrite(shm, p)) {
1230            return 1;
1231        }
1232    }
1233    for (i = 0; i < xc_var_hcache.size; i ++) {
1234        shm = xc_var_caches[i]->shm;
1235        if (shm->handlers->is_readwrite(shm, p)) {
1236            return 1;
1237        }
1238    }
1239    return 0;
1240}
1241/* }}} */
1242int xc_is_ro(const void *p) /* {{{ */
1243{
1244    xc_shm_t *shm;
1245    int i;
1246    if (!xc_initized) {
1247        return 0;
1248    }
1249    for (i = 0; i < xc_php_hcache.size; i ++) {
1250        shm = xc_php_caches[i]->shm;
1251        if (shm->handlers->is_readonly(shm, p)) {
1252            return 1;
1253        }
1254    }
1255    for (i = 0; i < xc_var_hcache.size; i ++) {
1256        shm = xc_var_caches[i]->shm;
1257        if (shm->handlers->is_readonly(shm, p)) {
1258            return 1;
1259        }
1260    }
1261    return 0;
1262}
1263/* }}} */
1264int xc_is_shm(const void *p) /* {{{ */
1265{
1266    return xc_is_ro(p) || xc_is_rw(p);
1267}
1268/* }}} */
1269
1270#ifdef ZEND_ENGINE_2
1271/* {{{ xc_gc_op_array_t */
1272typedef struct {
1273    zend_uint num_args;
1274    zend_arg_info *arg_info;
1275} xc_gc_op_array_t;
1276/* }}} */
1277void xc_gc_add_op_array(zend_op_array *op_array TSRMLS_DC) /* {{{ */
1278{
1279    xc_gc_op_array_t gc_op_array;
1280    gc_op_array.num_args = op_array->num_args;
1281    gc_op_array.arg_info = op_array->arg_info;
1282#ifdef ZEND_ENGINE_2
1283    zend_hash_next_index_insert(&XG(gc_op_arrays), (void *) &gc_op_array, sizeof(gc_op_array), NULL);
1284#endif
1285}
1286/* }}} */
1287static void xc_gc_op_array(void *pDest) /* {{{ */
1288{
1289    xc_gc_op_array_t *op_array = (xc_gc_op_array_t *) pDest;
1290    zend_uint i;
1291    if (op_array->arg_info) {
1292        for (i = 0; i < op_array->num_args; i++) {
1293            efree((char *) ZSTR_V(op_array->arg_info[i].name));
1294            if (ZSTR_V(op_array->arg_info[i].class_name)) {
1295                efree((char *) ZSTR_V(op_array->arg_info[i].class_name));
1296            }
1297        }
1298        efree(op_array->arg_info);
1299    }
1300}
1301/* }}} */
1302#endif
1303
1304/* module helper function */
1305static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
1306{
1307    typedef struct {
1308        const char *prefix;
1309        zend_uchar (*getsize)();
1310        const char *(*get)(zend_uchar i);
1311    } xc_meminfo_t;
1312    xc_meminfo_t nameinfos[] = {
1313        { "",        xc_get_op_type_count,   xc_get_op_type   },
1314        { "",        xc_get_data_type_count, xc_get_data_type },
1315        { "",        xc_get_opcode_count,    xc_get_opcode    },
1316        { "OPSPEC_", xc_get_op_spec_count,   xc_get_op_spec   },
1317        { NULL, NULL, NULL }
1318    };
1319    xc_meminfo_t* p;
1320    zend_uchar i, count;
1321    char const_name[96];
1322    int const_name_len;
1323    int undefdone = 0;
1324
1325    for (p = nameinfos; p->getsize; p ++) {
1326        count = p->getsize();
1327        for (i = 0; i < count; i ++) {
1328            const char *name = p->get(i);
1329            if (!name) continue;
1330            if (strcmp(name, "UNDEF") == 0) {
1331                if (undefdone) continue;
1332                undefdone = 1;
1333            }
1334            const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
1335            zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1336        }
1337    }
1338
1339    zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1340    zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1341    zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1342    zend_register_stringl_constant(ZEND_STRS("XCACHE_VERSION"), ZEND_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1343    zend_register_stringl_constant(ZEND_STRS("XCACHE_MODULES"), ZEND_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1344    return 0;
1345}
1346/* }}} */
1347static xc_shm_t *xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache) /* {{{ */
1348{
1349    int i;
1350    xc_cache_t *cache;
1351    xc_shm_t *shm;
1352
1353    if (!caches) {
1354        return NULL;
1355    }
1356    shm = NULL;
1357    for (i = 0; i < hcache->size; i ++) {
1358        cache = caches[i];
1359        if (cache) {
1360            if (cache->lck) {
1361                xc_lock_destroy(cache->lck);
1362            }
1363            /* do NOT free
1364            if (cache->entries) {
1365                cache->mem->handlers->free(cache->mem, cache->entries);
1366            }
1367            cache->mem->handlers->free(cache->mem, cache);
1368            */
1369            shm = cache->shm;
1370            shm->handlers->memdestroy(cache->mem);
1371        }
1372    }
1373    free(caches);
1374    return shm;
1375}
1376/* }}} */
1377static xc_cache_t **xc_cache_init(xc_shm_t *shm, xc_hash_t *hcache, xc_hash_t *hentry, xc_shmsize_t shmsize) /* {{{ */
1378{
1379    xc_cache_t **caches = NULL, *cache;
1380    xc_mem_t *mem;
1381    time_t now = time(NULL);
1382    int i;
1383    xc_memsize_t memsize;
1384
1385    memsize = shmsize / hcache->size;
1386
1387    /* Don't let it break out of mem after ALIGNed
1388     * This is important for
1389     * Simply loop until it fit our need
1390     */
1391    while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
1392        if (memsize < ALIGN(1)) {
1393            CHECK(NULL, "cache too small");
1394        }
1395        memsize --;
1396    }
1397
1398    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
1399
1400    for (i = 0; i < hcache->size; i ++) {
1401        CHECK(mem            = shm->handlers->meminit(shm, memsize), "Failed init memory allocator");
1402        CHECK(cache          = mem->handlers->calloc(mem, 1, sizeof(xc_cache_t)), "cache OOM");
1403        CHECK(cache->entries = mem->handlers->calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
1404        CHECK(cache->lck     = xc_lock_init(NULL), "can't create lock");
1405
1406        cache->hcache  = hcache;
1407        cache->hentry  = hentry;
1408        cache->shm     = shm;
1409        cache->mem     = mem;
1410        cache->cacheid = i;
1411        cache->last_gc_deletes = now;
1412        cache->last_gc_expires = now;
1413        caches[i] = cache;
1414    }
1415    return caches;
1416
1417err:
1418    if (caches) {
1419        xc_cache_destroy(caches, hcache);
1420    }
1421    return NULL;
1422}
1423/* }}} */
1424static void xc_destroy() /* {{{ */
1425{
1426    xc_shm_t *shm = NULL;
1427
1428    if (old_compile_file) {
1429        zend_compile_file = old_compile_file;
1430        old_compile_file = NULL;
1431    }
1432
1433    if (origin_compile_file) {
1434        zend_compile_file = origin_compile_file;
1435        origin_compile_file = NULL;
1436    }
1437
1438    if (xc_php_caches) {
1439        shm = xc_cache_destroy(xc_php_caches, &xc_php_hcache);
1440        xc_php_caches = NULL;
1441    }
1442
1443    if (xc_var_caches) {
1444        shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
1445        xc_var_caches = NULL;
1446    }
1447
1448    if (shm) {
1449        xc_shm_destroy(shm);
1450    }
1451
1452    xc_initized = 0;
1453}
1454/* }}} */
1455static int xc_init(int module_number TSRMLS_DC) /* {{{ */
1456{
1457    xc_shm_t *shm;
1458    xc_shmsize_t shmsize = ALIGN(xc_php_size) + ALIGN(xc_var_size);
1459
1460    xc_php_caches = xc_var_caches = NULL;
1461    shm = NULL;
1462
1463    if (shmsize < (size_t) xc_php_size || shmsize < (size_t) xc_var_size) {
1464        zend_error(E_ERROR, "XCache: neither xcache.size nor xcache.var_size can be negative");
1465        goto err;
1466    }
1467
1468    if (xc_php_size || xc_var_size) {
1469        CHECK(shm = xc_shm_init(xc_shm_scheme, shmsize, xc_readonly_protection, xc_mmap_path, NULL), "Cannot create shm");
1470        if (!shm->handlers->can_readonly(shm)) {
1471            xc_readonly_protection = 0;
1472        }
1473
1474        if (xc_php_size) {
1475            old_compile_file = zend_compile_file;
1476            zend_compile_file = xc_compile_file;
1477
1478            CHECK(xc_php_caches = xc_cache_init(shm, &xc_php_hcache, &xc_php_hentry, xc_php_size), "failed init opcode cache");
1479        }
1480
1481        if (xc_var_size) {
1482            CHECK(xc_var_caches = xc_cache_init(shm, &xc_var_hcache, &xc_var_hentry, xc_var_size), "failed init variable cache");
1483        }
1484    }
1485    return SUCCESS;
1486
1487err:
1488    xc_destroy();
1489    if (xc_php_caches || xc_var_caches) {
1490        /* shm destroied in xc_destroy() */
1491    }
1492    else if (shm) {
1493        xc_shm_destroy(shm);
1494    }
1495    return 0;
1496}
1497/* }}} */
1498static void xc_request_init(TSRMLS_D) /* {{{ */
1499{
1500    int i;
1501
1502    if (!XG(internal_table_copied)) {
1503        zend_function tmp_func;
1504        xc_cest_t tmp_cest;
1505
1506        zend_hash_destroy(&XG(internal_function_table));
1507        zend_hash_destroy(&XG(internal_class_table));
1508
1509        zend_hash_init_ex(&XG(internal_function_table), 100, NULL, NULL, 1, 0);
1510        zend_hash_init_ex(&XG(internal_class_table),    10,  NULL, NULL, 1, 0);
1511
1512        zend_hash_copy(&XG(internal_function_table), CG(function_table), NULL, &tmp_func, sizeof(tmp_func));
1513        zend_hash_copy(&XG(internal_class_table), CG(class_table), NULL, &tmp_cest, sizeof(tmp_cest));
1514
1515        XG(internal_table_copied) = 1;
1516    }
1517    if (xc_php_hcache.size && !XG(php_holds)) {
1518        XG(php_holds) = calloc(xc_php_hcache.size, sizeof(xc_stack_t));
1519        for (i = 0; i < xc_php_hcache.size; i ++) {
1520            xc_stack_init(&XG(php_holds[i]));
1521        }
1522    }
1523
1524    if (xc_initized && xc_var_hcache.size && !XG(var_holds)) {
1525        XG(var_holds) = calloc(xc_var_hcache.size, sizeof(xc_stack_t));
1526        for (i = 0; i < xc_var_hcache.size; i ++) {
1527            xc_stack_init(&XG(var_holds[i]));
1528        }
1529    }
1530
1531#ifdef ZEND_ENGINE_2
1532    zend_hash_init(&XG(gc_op_arrays), 32, NULL, xc_gc_op_array, 0);
1533#endif
1534
1535#if PHP_API_VERSION <= 20041225
1536    XG(request_time) = time(NULL);
1537#else
1538    XG(request_time) = sapi_get_request_time(TSRMLS_C);
1539#endif
1540
1541#ifdef HAVE_XCACHE_COVERAGER
1542    xc_coverager_request_init(TSRMLS_C);
1543#endif
1544}
1545/* }}} */
1546static void xc_request_shutdown(TSRMLS_D) /* {{{ */
1547{
1548    xc_entry_unholds(TSRMLS_C);
1549#ifdef ZEND_ENGINE_2
1550    zend_hash_destroy(&XG(gc_op_arrays));
1551#endif
1552    xc_gc_expires_php(TSRMLS_C);
1553    xc_gc_expires_var(TSRMLS_C);
1554    xc_gc_deletes(TSRMLS_C);
1555#ifdef HAVE_XCACHE_COVERAGER
1556    xc_coverager_request_shutdown(TSRMLS_C);
1557#endif
1558}
1559/* }}} */
1560/* {{{ PHP_GINIT_FUNCTION(xcache) */
1561static
1562#ifdef PHP_GINIT_FUNCTION
1563PHP_GINIT_FUNCTION(xcache)
1564#else
1565void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
1566#endif
1567{
1568    memset(xcache_globals, 0, sizeof(zend_xcache_globals));
1569
1570    zend_hash_init_ex(&xcache_globals->internal_function_table, 1, NULL, NULL, 1, 0);
1571    zend_hash_init_ex(&xcache_globals->internal_class_table,    1, NULL, NULL, 1, 0);
1572}
1573/* }}} */
1574/* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
1575static
1576#ifdef PHP_GSHUTDOWN_FUNCTION
1577PHP_GSHUTDOWN_FUNCTION(xcache)
1578#else
1579void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
1580#endif
1581{
1582    int i;
1583
1584    if (xcache_globals->php_holds != NULL) {
1585        for (i = 0; i < xc_php_hcache.size; i ++) {
1586            xc_stack_destroy(&xcache_globals->php_holds[i]);
1587        }
1588        free(xcache_globals->php_holds);
1589        xcache_globals->php_holds = NULL;
1590    }
1591
1592    if (xcache_globals->var_holds != NULL) {
1593        for (i = 0; i < xc_var_hcache.size; i ++) {
1594            xc_stack_destroy(&xcache_globals->var_holds[i]);
1595        }
1596        free(xcache_globals->var_holds);
1597        xcache_globals->var_holds = NULL;
1598    }
1599
1600    if (xcache_globals->internal_table_copied) {
1601        zend_hash_destroy(&xcache_globals->internal_function_table);
1602        zend_hash_destroy(&xcache_globals->internal_class_table);
1603    }
1604}
1605/* }}} */
1606
1607/* user functions */
1608static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
1609{
1610    zval **server = NULL;
1611    zval **user = NULL;
1612    zval **pass = NULL;
1613    char *admin_user = NULL;
1614    char *admin_pass = NULL;
1615    HashTable *ht;
1616
1617    /* auth disabled, nothing to do.. */
1618    if (!XG(auth_enabled)) {
1619        return 1;
1620    }
1621
1622    if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
1623        admin_user = NULL;
1624    }
1625    if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
1626        admin_pass = NULL;
1627    }
1628
1629    if (admin_user == NULL || admin_pass == NULL) {
1630        php_error_docref(NULL TSRMLS_CC, E_ERROR, "xcache.admin.user and xcache.admin.pass is required");
1631        zend_bailout();
1632    }
1633    if (strlen(admin_pass) != 32) {
1634        php_error_docref(NULL TSRMLS_CC, E_ERROR, "unexpect %lu bytes of xcache.admin.pass, expected 32 bytes, the password after md5()", (unsigned long) strlen(admin_pass));
1635        zend_bailout();
1636    }
1637
1638#ifdef ZEND_ENGINE_2_1
1639    zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
1640#endif
1641    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
1642        php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
1643        zend_bailout();
1644    }
1645    ht = HASH_OF((*server));
1646
1647    if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
1648        user = NULL;
1649    }
1650    else if (Z_TYPE_PP(user) != IS_STRING) {
1651        user = NULL;
1652    }
1653
1654    if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
1655        pass = NULL;
1656    }
1657    else if (Z_TYPE_PP(pass) != IS_STRING) {
1658        pass = NULL;
1659    }
1660
1661    if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
1662        PHP_MD5_CTX context;
1663        char md5str[33];
1664        unsigned char digest[16];
1665
1666        PHP_MD5Init(&context);
1667        PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
1668        PHP_MD5Final(digest, &context);
1669
1670        md5str[0] = '\0';
1671        make_digest(md5str, digest);
1672        if (strcmp(admin_pass, md5str) == 0) {
1673            return 1;
1674        }
1675    }
1676
1677#define STR "WWW-authenticate: Basic Realm=\"XCache Administration\""
1678    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1679#undef STR
1680#define STR "HTTP/1.0 401 Unauthorized"
1681    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1682#undef STR
1683    ZEND_PUTS("XCache Auth Failed. User and Password is case sense\n");
1684
1685    zend_bailout();
1686    return 0;
1687}
1688/* }}} */
1689/* {{{ xcache_admin_operate */
1690typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
1691static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
1692{
1693    long type;
1694    int size;
1695    xc_cache_t **caches, *cache;
1696    long id = 0;
1697
1698    xcache_admin_auth_check(TSRMLS_C);
1699
1700    if (!xc_initized) {
1701        RETURN_NULL();
1702    }
1703
1704    if (optype == XC_OP_COUNT) {
1705        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
1706            return;
1707        }
1708    }
1709    else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
1710        return;
1711    }
1712
1713    switch (type) {
1714        case XC_TYPE_PHP:
1715            size = xc_php_hcache.size;
1716            caches = xc_php_caches;
1717            break;
1718
1719        case XC_TYPE_VAR:
1720            size = xc_var_hcache.size;
1721            caches = xc_var_caches;
1722            break;
1723
1724        default:
1725            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
1726            RETURN_FALSE;
1727    }
1728
1729    switch (optype) {
1730        case XC_OP_COUNT:
1731            RETURN_LONG(size)
1732            break;
1733
1734        case XC_OP_INFO:
1735        case XC_OP_LIST:
1736            if (id < 0 || id >= size) {
1737                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1738                RETURN_FALSE;
1739            }
1740
1741            array_init(return_value);
1742
1743            cache = caches[id];
1744            ENTER_LOCK(cache) {
1745                if (optype == XC_OP_INFO) {
1746                    xc_fillinfo_dmz(type, cache, return_value TSRMLS_CC);
1747                }
1748                else {
1749                    xc_filllist_dmz(cache, return_value TSRMLS_CC);
1750                }
1751            } LEAVE_LOCK(cache);
1752            break;
1753        case XC_OP_CLEAR:
1754            {
1755                xc_entry_t *e, *next;
1756                int i, c;
1757
1758                if (id < 0 || id >= size) {
1759                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1760                    RETURN_FALSE;
1761                }
1762
1763                cache = caches[id];
1764                ENTER_LOCK(cache) {
1765                    for (i = 0, c = cache->hentry->size; i < c; i ++) {
1766                        for (e = cache->entries[i]; e; e = next) {
1767                            next = e->next;
1768                            xc_entry_remove_dmz(e TSRMLS_CC);
1769                        }
1770                        cache->entries[i] = NULL;
1771                    }
1772                } LEAVE_LOCK(cache);
1773                xc_gc_deletes(TSRMLS_C);
1774            }
1775            break;
1776
1777        default:
1778            assert(0);
1779    }
1780}
1781/* }}} */
1782/* {{{ proto int xcache_count(int type)
1783   Return count of cache on specified cache type */
1784PHP_FUNCTION(xcache_count)
1785{
1786    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1787}
1788/* }}} */
1789/* {{{ proto array xcache_info(int type, int id)
1790   Get cache info by id on specified cache type */
1791PHP_FUNCTION(xcache_info)
1792{
1793    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1794}
1795/* }}} */
1796/* {{{ proto array xcache_list(int type, int id)
1797   Get cache entries list by id on specified cache type */
1798PHP_FUNCTION(xcache_list)
1799{
1800    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1801}
1802/* }}} */
1803/* {{{ proto array xcache_clear_cache(int type, int id)
1804   Clear cache by id on specified cache type */
1805PHP_FUNCTION(xcache_clear_cache)
1806{
1807    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1808}
1809/* }}} */
1810
1811#define VAR_DISABLED_WARNING() do { \
1812        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.var_size is either 0 or too small to enable var data caching"); \
1813} while (0)
1814
1815static int xc_entry_init_key_var(xc_entry_t *xce, zval *name TSRMLS_DC) /* {{{ */
1816{
1817    xc_hash_value_t hv;
1818    int cacheid;
1819
1820    switch (Z_TYPE_P(name)) {
1821#ifdef IS_UNICODE
1822        case IS_UNICODE:
1823#endif
1824        case IS_STRING:
1825            break;
1826        default:
1827#ifdef IS_UNICODE
1828            convert_to_text(name);
1829#else
1830            convert_to_string(name);
1831#endif
1832    }
1833#ifdef IS_UNICODE
1834    xce->name_type = name->type;
1835#endif
1836    xce->name = name->value;
1837
1838    hv = xc_entry_hash_var(xce TSRMLS_CC);
1839
1840    cacheid = (hv & xc_var_hcache.mask);
1841    xce->cache = xc_var_caches[cacheid];
1842    hv >>= xc_var_hcache.bits;
1843    xce->hvalue = (hv & xc_var_hentry.mask);
1844
1845    xce->type = XC_TYPE_VAR;
1846    return SUCCESS;
1847}
1848/* }}} */
1849/* {{{ proto mixed xcache_get(string name)
1850   Get cached data by specified name */
1851PHP_FUNCTION(xcache_get)
1852{
1853    xc_entry_t xce, *stored_xce;
1854    xc_entry_data_var_t var;
1855    zval *name;
1856    int found = 0;
1857
1858    if (!xc_var_caches) {
1859        VAR_DISABLED_WARNING();
1860        RETURN_NULL();
1861    }
1862
1863    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1864        return;
1865    }
1866    xce.data.var = &var;
1867    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1868
1869    ENTER_LOCK(xce.cache) {
1870        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1871        if (stored_xce) {
1872            if (!VAR_ENTRY_EXPIRED(stored_xce)) {
1873                found = 1;
1874                xc_processor_restore_zval(return_value, stored_xce->data.var->value, stored_xce->have_references TSRMLS_CC);
1875                /* return */
1876                break;
1877            }
1878            else {
1879                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1880            }
1881        }
1882
1883        RETVAL_NULL();
1884    } LEAVE_LOCK(xce.cache);
1885    if (found) {
1886        xce.cache->hits ++;
1887    }
1888    else {
1889        xce.cache->misses ++;
1890    }
1891}
1892/* }}} */
1893/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
1894   Store data to cache by specified name */
1895PHP_FUNCTION(xcache_set)
1896{
1897    xc_entry_t xce, *stored_xce;
1898    xc_entry_data_var_t var;
1899    zval *name;
1900    zval *value;
1901
1902    if (!xc_var_caches) {
1903        VAR_DISABLED_WARNING();
1904        RETURN_NULL();
1905    }
1906
1907    xce.ttl = XG(var_ttl);
1908    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &xce.ttl) == FAILURE) {
1909        return;
1910    }
1911
1912    /* max ttl */
1913    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
1914        xce.ttl = xc_var_maxttl;
1915    }
1916
1917    xce.data.var = &var;
1918    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1919
1920    ENTER_LOCK(xce.cache) {
1921        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1922        if (stored_xce) {
1923            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1924        }
1925        var.value = value;
1926        RETVAL_BOOL(xc_entry_store_dmz(&xce TSRMLS_CC) != NULL ? 1 : 0);
1927    } LEAVE_LOCK(xce.cache);
1928}
1929/* }}} */
1930/* {{{ proto bool  xcache_isset(string name)
1931   Check if an entry exists in cache by specified name */
1932PHP_FUNCTION(xcache_isset)
1933{
1934    xc_entry_t xce, *stored_xce;
1935    xc_entry_data_var_t var;
1936    zval *name;
1937    int found = 0;
1938
1939    if (!xc_var_caches) {
1940        VAR_DISABLED_WARNING();
1941        RETURN_FALSE;
1942    }
1943
1944    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1945        return;
1946    }
1947    xce.data.var = &var;
1948    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1949
1950    ENTER_LOCK(xce.cache) {
1951        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1952        if (stored_xce) {
1953            if (!VAR_ENTRY_EXPIRED(stored_xce)) {
1954                found = 1;
1955                RETVAL_TRUE;
1956                /* return */
1957                break;
1958            }
1959            else {
1960                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1961            }
1962        }
1963
1964        RETVAL_FALSE;
1965    } LEAVE_LOCK(xce.cache);
1966    if (found) {
1967        xce.cache->hits ++;
1968    }
1969    else {
1970        xce.cache->misses ++;
1971    }
1972}
1973/* }}} */
1974/* {{{ proto bool  xcache_unset(string name)
1975   Unset existing data in cache by specified name */
1976PHP_FUNCTION(xcache_unset)
1977{
1978    xc_entry_t xce, *stored_xce;
1979    xc_entry_data_var_t var;
1980    zval *name;
1981
1982    if (!xc_var_caches) {
1983        VAR_DISABLED_WARNING();
1984        RETURN_FALSE;
1985    }
1986
1987    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1988        return;
1989    }
1990    xce.data.var = &var;
1991    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1992
1993    ENTER_LOCK(xce.cache) {
1994        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1995        if (stored_xce) {
1996            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1997            RETVAL_TRUE;
1998        }
1999        else {
2000            RETVAL_FALSE;
2001        }
2002    } LEAVE_LOCK(xce.cache);
2003}
2004/* }}} */
2005static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
2006{
2007    xc_entry_t xce, *stored_xce;
2008    xc_entry_data_var_t var, *stored_var;
2009    zval *name;
2010    long count = 1;
2011    long value = 0;
2012    zval oldzval;
2013
2014    if (!xc_var_caches) {
2015        VAR_DISABLED_WARNING();
2016        RETURN_NULL();
2017    }
2018
2019    xce.ttl = XG(var_ttl);
2020    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &xce.ttl) == FAILURE) {
2021        return;
2022    }
2023
2024    /* max ttl */
2025    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
2026        xce.ttl = xc_var_maxttl;
2027    }
2028
2029    xce.data.var = &var;
2030    xc_entry_init_key_var(&xce, name TSRMLS_CC);
2031
2032    ENTER_LOCK(xce.cache) {
2033        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
2034        if (stored_xce) {
2035            TRACE("incdec: gotxce %s", xce.name.str.val);
2036            /* timeout */
2037            if (VAR_ENTRY_EXPIRED(stored_xce)) {
2038                TRACE("%s", "incdec: expired");
2039                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
2040                stored_xce = NULL;
2041            }
2042            else {
2043                /* do it in place */
2044                stored_var = stored_xce->data.var;
2045                if (Z_TYPE_P(stored_var->value) == IS_LONG) {
2046                    zval *zv;
2047                    stored_xce->ctime = XG(request_time);
2048                    stored_xce->ttl   = xce.ttl;
2049                    TRACE("%s", "incdec: islong");
2050                    value = Z_LVAL_P(stored_var->value);
2051                    value += (inc == 1 ? count : - count);
2052                    RETVAL_LONG(value);
2053
2054                    zv = (zval *) xce.cache->shm->handlers->to_readwrite(xce.cache->shm, (char *) stored_var->value);
2055                    Z_LVAL_P(zv) = value;
2056                    break; /* leave lock */
2057                }
2058                else {
2059                    TRACE("%s", "incdec: notlong");
2060                    xc_processor_restore_zval(&oldzval, stored_xce->data.var->value, stored_xce->have_references TSRMLS_CC);
2061                    convert_to_long(&oldzval);
2062                    value = Z_LVAL(oldzval);
2063                    zval_dtor(&oldzval);
2064                }
2065            }
2066        }
2067        else {
2068            TRACE("incdec: %s not found", xce.name.str.val);
2069        }
2070
2071        value += (inc == 1 ? count : - count);
2072        RETVAL_LONG(value);
2073        var.value = return_value;
2074
2075        if (stored_xce) {
2076            xce.atime = stored_xce->atime;
2077            xce.ctime = stored_xce->ctime;
2078            xce.hits  = stored_xce->hits;
2079            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
2080        }
2081        xc_entry_store_dmz(&xce TSRMLS_CC);
2082
2083    } LEAVE_LOCK(xce.cache);
2084}
2085/* }}} */
2086/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
2087   Increase an int counter in cache by specified name, create it if not exists */
2088PHP_FUNCTION(xcache_inc)
2089{
2090    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2091}
2092/* }}} */
2093/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
2094   Decrease an int counter in cache by specified name, create it if not exists */
2095PHP_FUNCTION(xcache_dec)
2096{
2097    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2098}
2099/* }}} */
2100/* {{{ proto string xcache_asm(string filename)
2101 */
2102#ifdef HAVE_XCACHE_ASSEMBLER
2103PHP_FUNCTION(xcache_asm)
2104{
2105}
2106#endif
2107/* }}} */
2108#ifdef HAVE_XCACHE_DISASSEMBLER
2109/* {{{ proto array  xcache_dasm_file(string filename)
2110   Disassemble file into opcode array by filename */
2111PHP_FUNCTION(xcache_dasm_file)
2112{
2113    char *filename;
2114    int filename_len;
2115
2116    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
2117        return;
2118    }
2119    if (!filename_len) RETURN_FALSE;
2120
2121    xc_dasm_file(return_value, filename TSRMLS_CC);
2122}
2123/* }}} */
2124/* {{{ proto array  xcache_dasm_string(string code)
2125   Disassemble php code into opcode array */
2126PHP_FUNCTION(xcache_dasm_string)
2127{
2128    zval *code;
2129
2130    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
2131        return;
2132    }
2133    xc_dasm_string(return_value, code TSRMLS_CC);
2134}
2135/* }}} */
2136#endif
2137/* {{{ proto string xcache_encode(string filename)
2138   Encode php file into XCache opcode encoded format */
2139#ifdef HAVE_XCACHE_ENCODER
2140PHP_FUNCTION(xcache_encode)
2141{
2142}
2143#endif
2144/* }}} */
2145/* {{{ proto bool xcache_decode_file(string filename)
2146   Decode(load) opcode from XCache encoded format file */
2147#ifdef HAVE_XCACHE_DECODER
2148PHP_FUNCTION(xcache_decode_file)
2149{
2150}
2151#endif
2152/* }}} */
2153/* {{{ proto bool xcache_decode_string(string data)
2154   Decode(load) opcode from XCache encoded format data */
2155#ifdef HAVE_XCACHE_DECODER
2156PHP_FUNCTION(xcache_decode_string)
2157{
2158}
2159#endif
2160/* }}} */
2161/* {{{ xc_call_getter */
2162typedef const char *(xc_name_getter_t)(zend_uchar type);
2163static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
2164{
2165    long spec;
2166    const char *name;
2167
2168    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
2169        return;
2170    }
2171    if (spec >= 0 && spec < count) {
2172        name = getter((zend_uchar) spec);
2173        if (name) {
2174            /* RETURN_STRING */
2175            int len = strlen(name);
2176            return_value->value.str.len = len;
2177            return_value->value.str.val = estrndup(name, len);
2178            return_value->type = IS_STRING; 
2179            return;
2180        }
2181    }
2182    RETURN_NULL();
2183}
2184/* }}} */
2185/* {{{ proto string xcache_get_op_type(int op_type) */
2186PHP_FUNCTION(xcache_get_op_type)
2187{
2188    xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2189}
2190/* }}} */
2191/* {{{ proto string xcache_get_data_type(int type) */
2192PHP_FUNCTION(xcache_get_data_type)
2193{
2194    xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2195}
2196/* }}} */
2197/* {{{ proto string xcache_get_opcode(int opcode) */
2198PHP_FUNCTION(xcache_get_opcode)
2199{
2200    xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2201}
2202/* }}} */
2203/* {{{ proto string xcache_get_op_spec(int op_type) */
2204PHP_FUNCTION(xcache_get_op_spec)
2205{
2206    xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2207}
2208/* }}} */
2209#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
2210/* {{{ proto string xcache_get_opcode_spec(int opcode) */
2211PHP_FUNCTION(xcache_get_opcode_spec)
2212{
2213    long spec;
2214    const xc_opcode_spec_t *opspec;
2215
2216    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
2217        return;
2218    }
2219    if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
2220        opspec = xc_get_opcode_spec((zend_uchar) spec);
2221        if (opspec) {
2222            array_init(return_value);
2223            add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
2224            add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
2225            add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
2226            add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
2227            return;
2228        }
2229    }
2230    RETURN_NULL();
2231}
2232/* }}} */
2233#endif
2234/* {{{ proto mixed xcache_get_special_value(zval value) */
2235PHP_FUNCTION(xcache_get_special_value)
2236{
2237    zval *value;
2238
2239    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
2240        return;
2241    }
2242
2243    if (value->type == IS_CONSTANT) {
2244        *return_value = *value;
2245        zval_copy_ctor(return_value);
2246        return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
2247        return;
2248    }
2249
2250    if (value->type == IS_CONSTANT_ARRAY) {
2251        *return_value = *value;
2252        zval_copy_ctor(return_value);
2253        return_value->type = IS_ARRAY;
2254        return;
2255    }
2256
2257    RETURN_NULL();
2258}
2259/* }}} */
2260/* {{{ proto string xcache_coredump(int op_type) */
2261PHP_FUNCTION(xcache_coredump)
2262{
2263    if (xc_test) {
2264        raise(SIGSEGV);
2265    }
2266    else {
2267        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
2268    }
2269}
2270/* }}} */
2271/* {{{ proto string xcache_is_autoglobal(string name) */
2272PHP_FUNCTION(xcache_is_autoglobal)
2273{
2274    char *name;
2275    int name_len;
2276
2277    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
2278        return;
2279    }
2280
2281    RETURN_BOOL(zend_hash_exists(CG(auto_globals), name, name_len + 1));
2282}
2283/* }}} */
2284static function_entry xcache_functions[] = /* {{{ */
2285{
2286    PHP_FE(xcache_count,             NULL)
2287    PHP_FE(xcache_info,              NULL)
2288    PHP_FE(xcache_list,              NULL)
2289    PHP_FE(xcache_clear_cache,       NULL)
2290    PHP_FE(xcache_coredump,          NULL)
2291#ifdef HAVE_XCACHE_ASSEMBLER
2292    PHP_FE(xcache_asm,               NULL)
2293#endif
2294#ifdef HAVE_XCACHE_DISASSEMBLER
2295    PHP_FE(xcache_dasm_file,         NULL)
2296    PHP_FE(xcache_dasm_string,       NULL)
2297#endif
2298#ifdef HAVE_XCACHE_ENCODER
2299    PHP_FE(xcache_encode,            NULL)
2300#endif
2301#ifdef HAVE_XCACHE_DECODER
2302    PHP_FE(xcache_decode_file,       NULL)
2303    PHP_FE(xcache_decode_string,     NULL)
2304#endif
2305#ifdef HAVE_XCACHE_COVERAGER
2306    PHP_FE(xcache_coverager_decode,  NULL)
2307    PHP_FE(xcache_coverager_start,   NULL)
2308    PHP_FE(xcache_coverager_stop,    NULL)
2309    PHP_FE(xcache_coverager_get,     NULL)
2310#endif
2311    PHP_FE(xcache_get_special_value, NULL)
2312    PHP_FE(xcache_get_op_type,       NULL)
2313    PHP_FE(xcache_get_data_type,     NULL)
2314    PHP_FE(xcache_get_opcode,        NULL)
2315#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
2316    PHP_FE(xcache_get_opcode_spec,   NULL)
2317#endif
2318    PHP_FE(xcache_is_autoglobal,     NULL)
2319    PHP_FE(xcache_inc,               NULL)
2320    PHP_FE(xcache_dec,               NULL)
2321    PHP_FE(xcache_get,               NULL)
2322    PHP_FE(xcache_set,               NULL)
2323    PHP_FE(xcache_isset,             NULL)
2324    PHP_FE(xcache_unset,             NULL)
2325    {NULL, NULL,                     NULL}
2326};
2327/* }}} */
2328
2329/* old signal handlers {{{ */
2330typedef void (*xc_sighandler_t)(int);
2331#define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
2332#include "foreachcoresig.h"
2333#undef FOREACH_SIG
2334/* }}} */
2335static void xcache_signal_handler(int sig);
2336static void xcache_restore_signal_handler() /* {{{ */
2337{
2338#define FOREACH_SIG(sig) do { \
2339    if (old_##sig##_handler != xcache_signal_handler) { \
2340        signal(sig, old_##sig##_handler); \
2341    } \
2342    else { \
2343        signal(sig, SIG_DFL); \
2344    } \
2345} while (0)
2346#include "foreachcoresig.h"
2347#undef FOREACH_SIG
2348}
2349/* }}} */
2350static void xcache_init_signal_handler() /* {{{ */
2351{
2352#define FOREACH_SIG(sig) \
2353    old_##sig##_handler = signal(sig, xcache_signal_handler)
2354#include "foreachcoresig.h"
2355#undef FOREACH_SIG
2356}
2357/* }}} */
2358static void xcache_signal_handler(int sig) /* {{{ */
2359{
2360    xcache_restore_signal_handler();
2361    if (xc_coredump_dir && xc_coredump_dir[0]) {
2362        chdir(xc_coredump_dir);
2363    }
2364    raise(sig);
2365}
2366/* }}} */
2367
2368/* {{{ PHP_INI */
2369
2370static PHP_INI_MH(xc_OnUpdateDummy)
2371{
2372    return SUCCESS;
2373}
2374
2375static PHP_INI_MH(xc_OnUpdateULong)
2376{
2377    zend_ulong *p = (zend_ulong *) mh_arg1;
2378
2379    *p = (zend_ulong) atoi(new_value);
2380    return SUCCESS;
2381}
2382
2383static PHP_INI_MH(xc_OnUpdateBool)
2384{
2385    zend_bool *p = (zend_bool *)mh_arg1;
2386
2387    if (strncasecmp("on", new_value, sizeof("on"))) {
2388        *p = (zend_bool) atoi(new_value);
2389    }
2390    else {
2391        *p = (zend_bool) 1;
2392    }
2393    return SUCCESS;
2394}
2395
2396static PHP_INI_MH(xc_OnUpdateString)
2397{
2398    char **p = (char**)mh_arg1;
2399    if (*p) {
2400        pefree(*p, 1);
2401    }
2402    *p = pemalloc(strlen(new_value) + 1, 1);
2403    strcpy(*p, new_value);
2404    return SUCCESS;
2405}
2406
2407#ifndef ZEND_ENGINE_2
2408#define OnUpdateLong OnUpdateInt
2409#endif
2410
2411#ifdef ZEND_WIN32
2412#   define DEFAULT_PATH "xcache"
2413#else
2414#   define DEFAULT_PATH "/dev/zero"
2415#endif
2416PHP_INI_BEGIN()
2417    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_mmap_path)
2418    PHP_INI_ENTRY1     ("xcache.coredump_directory",      "", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coredump_dir)
2419    PHP_INI_ENTRY1     ("xcache.test",                   "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_test)
2420    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_readonly_protection)
2421    /* opcode cache */
2422    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2423    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2424    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2425    PHP_INI_ENTRY1     ("xcache.shm_scheme",          "mmap", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_shm_scheme)
2426    PHP_INI_ENTRY1     ("xcache.ttl",                    "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_ttl)
2427    PHP_INI_ENTRY1     ("xcache.gc_interval",            "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_gc_interval)
2428    /* var cache */
2429    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2430    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2431    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2432    PHP_INI_ENTRY1     ("xcache.var_maxttl",             "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_maxttl)
2433    PHP_INI_ENTRY1     ("xcache.var_gc_interval",      "120", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_gc_interval)
2434
2435    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,        cacher,            zend_xcache_globals, xcache_globals)
2436    STD_PHP_INI_BOOLEAN("xcache.stat",                   "1", PHP_INI_ALL,    OnUpdateBool,        stat,              zend_xcache_globals, xcache_globals)
2437    STD_PHP_INI_BOOLEAN("xcache.admin.enable_auth",      "1", PHP_INI_SYSTEM, OnUpdateBool,        auth_enabled,      zend_xcache_globals, xcache_globals)
2438#ifdef HAVE_XCACHE_OPTIMIZER
2439    STD_PHP_INI_BOOLEAN("xcache.optimizer",              "0", PHP_INI_ALL,    OnUpdateBool,        optimizer,         zend_xcache_globals, xcache_globals)
2440#endif
2441    STD_PHP_INI_ENTRY  ("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong,        var_ttl,           zend_xcache_globals, xcache_globals)
2442#ifdef HAVE_XCACHE_COVERAGER
2443    STD_PHP_INI_BOOLEAN("xcache.coverager"      ,        "0", PHP_INI_ALL,    OnUpdateBool,        coverager,         zend_xcache_globals, xcache_globals)
2444    PHP_INI_ENTRY1     ("xcache.coveragedump_directory",  "", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2445#endif
2446PHP_INI_END()
2447/* }}} */
2448/* {{{ PHP_MINFO_FUNCTION(xcache) */
2449static PHP_MINFO_FUNCTION(xcache)
2450{
2451    char buf[100];
2452    char *ptr;
2453    int left, len;
2454    xc_shm_scheme_t *scheme;
2455    char *covdumpdir;
2456
2457    php_info_print_table_start();
2458    php_info_print_table_header(2, "XCache Support", "enabled");
2459    php_info_print_table_row(2, "Version", XCACHE_VERSION);
2460    php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
2461    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A");
2462
2463    if (xc_php_size) {
2464        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
2465        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
2466        php_info_print_table_row(2, "Opcode Cache", buf);
2467        efree(ptr);
2468    }
2469    else {
2470        php_info_print_table_row(2, "Opcode Cache", "disabled");
2471    }
2472    if (xc_var_size) {
2473        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
2474        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
2475        php_info_print_table_row(2, "Variable Cache", buf);
2476        efree(ptr);
2477    }
2478    else {
2479        php_info_print_table_row(2, "Variable Cache", "disabled");
2480    }
2481
2482    left = sizeof(buf);
2483    ptr = buf;
2484    buf[0] = '\0';
2485    for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
2486        len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
2487        left -= len;
2488        ptr += len;
2489    }
2490    php_info_print_table_row(2, "Shared Memory Schemes", buf);
2491
2492#ifdef HAVE_XCACHE_COVERAGER
2493    if (cfg_get_string("xcache.coveragedump_directory", &covdumpdir) != SUCCESS || !covdumpdir[0]) {
2494        covdumpdir = NULL;
2495    }
2496    php_info_print_table_row(2, "Coverage Auto Dumper", XG(coverager) && covdumpdir ? "enabled" : "disabled");
2497#endif
2498    php_info_print_table_end();
2499
2500    DISPLAY_INI_ENTRIES();
2501}
2502/* }}} */
2503/* {{{ extension startup */
2504static void xc_zend_extension_register(zend_extension *new_extension, DL_HANDLE handle)
2505{
2506    zend_extension extension;
2507
2508    extension = *new_extension;
2509    extension.handle = handle;
2510
2511    zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
2512
2513    zend_llist_prepend_element(&zend_extensions, &extension);
2514    TRACE("%s", "registered");
2515}
2516
2517static zend_llist_element *xc_llist_get_element_by_zend_extension(zend_llist *l, const char *extension_name)
2518{
2519    zend_llist_element *element;
2520
2521    for (element = zend_extensions.head; element; element = element->next) {
2522        zend_extension *extension = (zend_extension *) element->data;
2523
2524        if (!strcmp(extension->name, extension_name)) {
2525            return element;
2526        }
2527    }
2528    return NULL;
2529}
2530
2531static void xc_llist_prepend(zend_llist *l, zend_llist_element *element)
2532{
2533    element->next = l->head;
2534    element->prev = NULL;
2535    if (l->head) {
2536        l->head->prev = element;
2537    }
2538    else {
2539        l->tail = element;
2540    }
2541    l->head = element;
2542    ++l->count;
2543}
2544
2545static void xc_llist_unlink(zend_llist *l, zend_llist_element *element)
2546{
2547    if ((element)->prev) {
2548        (element)->prev->next = (element)->next;
2549    }
2550    else {
2551        (l)->head = (element)->next;
2552    }
2553
2554    if ((element)->next) {
2555        (element)->next->prev = (element)->prev;
2556    }
2557    else {
2558        (l)->tail = (element)->prev;
2559    }
2560
2561    --l->count;
2562}
2563
2564static int xc_zend_extension_startup(zend_extension *extension)
2565{
2566    if (extension->startup) {
2567        if (extension->startup(extension) != SUCCESS) {
2568            return FAILURE;
2569        }
2570    }
2571    return SUCCESS;
2572}
2573/* }}} */
2574static int xc_ptr_compare_func(void *p1, void *p2) /* {{{ */
2575{
2576    return p1 == p2;
2577}
2578/* }}} */
2579static int xc_zend_remove_extension(zend_extension *extension) /* {{{ */
2580{
2581    llist_dtor_func_t dtor;
2582
2583    assert(extension);
2584    dtor = zend_extensions.dtor; /* avoid dtor */
2585    zend_extensions.dtor = NULL;
2586    zend_llist_del_element(&zend_extensions, extension, xc_ptr_compare_func);
2587    zend_extensions.dtor = dtor;
2588    return SUCCESS;
2589}
2590/* }}} */
2591static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
2592{
2593    int bits, size;
2594    char *value;
2595
2596    if (cfg_get_string(name, &value) != SUCCESS) {
2597        value = default_value;
2598    }
2599
2600    p->size = zend_atoi(value, strlen(value));
2601    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
2602        /* empty body */
2603    }
2604    p->size = size;
2605    p->bits = bits;
2606    p->mask = size - 1;
2607
2608    return SUCCESS;
2609}
2610/* }}} */
2611static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
2612{
2613    char *value;
2614
2615    if (cfg_get_string(name, &value) != SUCCESS) {
2616        value = default_value;
2617    }
2618
2619    *p = zend_atoi(value, strlen(value));
2620    return SUCCESS;
2621}
2622/* }}} */
2623/* {{{ PHP_MINIT_FUNCTION(xcache) */
2624static PHP_MINIT_FUNCTION(xcache)
2625{
2626    char *env;
2627    zend_extension *ext;
2628    zend_llist_position lpos;
2629
2630    xc_module_gotup = 1;
2631    if (!xc_zend_extension_gotup) {
2632        xc_zend_extension_register(&zend_extension_entry, 0);
2633        xc_zend_extension_startup(&zend_extension_entry);
2634        xc_zend_extension_faked = 1;
2635    }
2636
2637    ext = zend_get_extension("Zend Optimizer");
2638    if (ext) {
2639        /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
2640        ext->op_array_handler = NULL;
2641    }
2642    /* cache if there's an op_array_ctor */
2643    for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
2644            ext;
2645            ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
2646        if (ext->op_array_ctor) {
2647            xc_have_op_array_ctor = 1;
2648            break;
2649        }
2650    }
2651
2652
2653#ifndef PHP_GINIT
2654    ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
2655#endif
2656    REGISTER_INI_ENTRIES();
2657
2658    if (strcmp(sapi_module.name, "cli") == 0) {
2659        if ((env = getenv("XCACHE_TEST")) != NULL) {
2660            zend_alter_ini_entry("xcache.test", sizeof("xcache.test"), env, strlen(env) + 1, PHP_INI_SYSTEM, PHP_INI_STAGE_STARTUP);
2661        }
2662        if (!xc_test) {
2663            /* disable cache for cli except for test */
2664            xc_php_size = xc_var_size = 0;
2665        }
2666    }
2667
2668    xc_config_long(&xc_php_size,       "xcache.size",        "0");
2669    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
2670    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
2671
2672    xc_config_long(&xc_var_size,       "xcache.var_size",    "0");
2673    xc_config_hash(&xc_var_hcache,     "xcache.var_count",   "1");
2674    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",  "8K");
2675
2676    if (xc_php_size <= 0) {
2677        xc_php_size = xc_php_hcache.size = 0;
2678    }
2679    if (xc_var_size <= 0) {
2680        xc_var_size = xc_var_hcache.size = 0;
2681    }
2682
2683    if (xc_coredump_dir && xc_coredump_dir[0]) {
2684        xcache_init_signal_handler();
2685    }
2686
2687    xc_init_constant(module_number TSRMLS_CC);
2688    xc_shm_init_modules();
2689
2690    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
2691        if (xc_init(module_number TSRMLS_CC) != SUCCESS) {
2692            zend_error(E_ERROR, "XCache: Cannot init");
2693            goto err_init;
2694        }
2695        xc_initized = 1;
2696    }
2697
2698#ifdef HAVE_XCACHE_COVERAGER
2699    xc_coverager_init(module_number TSRMLS_CC);
2700#endif
2701
2702    return SUCCESS;
2703
2704err_init:
2705    return FAILURE;
2706}
2707/* }}} */
2708/* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
2709static PHP_MSHUTDOWN_FUNCTION(xcache)
2710{
2711    if (xc_initized) {
2712        xc_destroy();
2713    }
2714    if (xc_mmap_path) {
2715        pefree(xc_mmap_path, 1);
2716        xc_mmap_path = NULL;
2717    }
2718    if (xc_shm_scheme) {
2719        pefree(xc_shm_scheme, 1);
2720        xc_shm_scheme = NULL;
2721    }
2722
2723#ifdef HAVE_XCACHE_COVERAGER
2724    xc_coverager_destroy();
2725#endif
2726
2727    if (xc_coredump_dir && xc_coredump_dir[0]) {
2728        xcache_restore_signal_handler();
2729    }
2730    if (xc_coredump_dir) {
2731        pefree(xc_coredump_dir, 1);
2732        xc_coredump_dir = NULL;
2733    }
2734#ifndef PHP_GINIT
2735#   ifdef ZTS
2736    ts_free_id(xcache_globals_id);
2737#   else
2738    xc_shutdown_globals(&xcache_globals TSRMLS_CC);
2739#   endif
2740#endif
2741
2742    if (xc_zend_extension_faked) {
2743        zend_extension *ext = zend_get_extension(XCACHE_NAME);
2744        if (ext->shutdown) {
2745            ext->shutdown(ext);
2746        }
2747        xc_zend_remove_extension(ext);
2748    }
2749    UNREGISTER_INI_ENTRIES();
2750
2751    xc_module_gotup = 0;
2752    xc_zend_extension_gotup = 0;
2753    xc_zend_extension_faked = 0;
2754
2755    return SUCCESS;
2756}
2757/* }}} */
2758/* {{{ PHP_RINIT_FUNCTION(xcache) */
2759static PHP_RINIT_FUNCTION(xcache)
2760{
2761    xc_request_init(TSRMLS_C);
2762    return SUCCESS;
2763}
2764/* }}} */
2765/* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
2766#ifndef ZEND_ENGINE_2
2767static PHP_RSHUTDOWN_FUNCTION(xcache)
2768#else
2769static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
2770#endif
2771{
2772#ifdef ZEND_ENGINE_2
2773    TSRMLS_FETCH();
2774#endif
2775
2776    xc_request_shutdown(TSRMLS_C);
2777    return SUCCESS;
2778}
2779/* }}} */
2780/* {{{ module dependencies */
2781#if ZEND_MODULE_API_NO >= 20050922
2782static const zend_module_dep xcache_module_deps[] = {
2783    ZEND_MOD_REQUIRED("standard")
2784    ZEND_MOD_CONFLICTS("apc")
2785    ZEND_MOD_CONFLICTS("eAccelerator")
2786    ZEND_MOD_CONFLICTS("Turck MMCache")
2787    {NULL, NULL, NULL}
2788};
2789#endif
2790/* }}} */ 
2791/* {{{ module definition structure */
2792
2793zend_module_entry xcache_module_entry = {
2794#if ZEND_MODULE_API_NO >= 20050922
2795    STANDARD_MODULE_HEADER_EX,
2796    NULL,
2797    xcache_module_deps,
2798#else
2799    STANDARD_MODULE_HEADER,
2800#endif
2801    XCACHE_NAME,
2802    xcache_functions,
2803    PHP_MINIT(xcache),
2804    PHP_MSHUTDOWN(xcache),
2805    PHP_RINIT(xcache),
2806#ifndef ZEND_ENGINE_2
2807    PHP_RSHUTDOWN(xcache),
2808#else
2809    NULL,
2810#endif
2811    PHP_MINFO(xcache),
2812    XCACHE_VERSION,
2813#ifdef PHP_GINIT
2814    PHP_MODULE_GLOBALS(xcache),
2815    PHP_GINIT(xcache),
2816    PHP_GSHUTDOWN(xcache),
2817#endif
2818#ifdef ZEND_ENGINE_2
2819    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
2820#else
2821    NULL,
2822    NULL,
2823#endif
2824    STANDARD_MODULE_PROPERTIES_EX
2825};
2826
2827#ifdef COMPILE_DL_XCACHE
2828ZEND_GET_MODULE(xcache)
2829#endif
2830/* }}} */
2831static startup_func_t xc_last_ext_startup;
2832static int xc_zend_startup_last(zend_extension *extension) /* {{{ */
2833{
2834    /* restore */
2835    extension->startup = xc_last_ext_startup;
2836    if (extension->startup) {
2837        if (extension->startup(extension) != SUCCESS) {
2838            return FAILURE;
2839        }
2840    }
2841    assert(xc_llist_zend_extension);
2842    xc_llist_prepend(&zend_extensions, xc_llist_zend_extension);
2843    if (!xc_module_gotup) {
2844        return zend_startup_module(&xcache_module_entry);
2845    }
2846    return SUCCESS;
2847}
2848/* }}} */
2849ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
2850{
2851    xc_zend_extension_gotup = 1;
2852
2853    if (!origin_compile_file) {
2854        origin_compile_file = zend_compile_file;
2855        zend_compile_file = xc_check_initial_compile_file;
2856    }
2857
2858    if (zend_llist_count(&zend_extensions) > 1) {
2859        zend_llist_position lpos;
2860        zend_extension *ext;
2861
2862        xc_llist_zend_extension = xc_llist_get_element_by_zend_extension(&zend_extensions, XCACHE_NAME);
2863        xc_llist_unlink(&zend_extensions, xc_llist_zend_extension);
2864
2865        ext = (zend_extension *) zend_llist_get_last_ex(&zend_extensions, &lpos);
2866        assert(ext && ext != xc_llist_zend_extension);
2867        xc_last_ext_startup = ext->startup;
2868        ext->startup = xc_zend_startup_last;
2869    }
2870    else if (!xc_module_gotup) {
2871        return zend_startup_module(&xcache_module_entry);
2872    }
2873    return SUCCESS;
2874}
2875/* }}} */
2876ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
2877{
2878    /* empty */
2879}
2880/* }}} */
2881ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
2882{
2883#ifdef HAVE_XCACHE_COVERAGER
2884    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
2885#endif
2886}
2887/* }}} */
2888ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
2889{
2890#if 0
2891    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
2892#endif
2893}
2894/* }}} */
2895ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
2896{
2897#if 0
2898    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
2899#endif
2900}
2901/* }}} */
2902/* {{{ zend extension definition structure */
2903ZEND_DLEXPORT zend_extension zend_extension_entry = {
2904    XCACHE_NAME,
2905    XCACHE_VERSION,
2906    XCACHE_AUTHOR,
2907    XCACHE_URL,
2908    XCACHE_COPYRIGHT,
2909    xcache_zend_startup,
2910    xcache_zend_shutdown,
2911    NULL,           /* activate_func_t */
2912    NULL,           /* deactivate_func_t */
2913    NULL,           /* message_handler_func_t */
2914    NULL,           /* op_array_handler_func_t */
2915    xcache_statement_handler,
2916    xcache_fcall_begin_handler,
2917    xcache_fcall_end_handler,
2918    NULL,           /* op_array_ctor_func_t */
2919    NULL,           /* op_array_dtor_func_t */
2920    STANDARD_ZEND_EXTENSION_PROPERTIES
2921};
2922
2923#ifndef ZEND_EXT_API
2924#   define ZEND_EXT_API ZEND_DLEXPORT
2925#endif
2926#if COMPILE_DL_XCACHE
2927ZEND_EXTENSION();
2928#endif
2929/* }}} */
Note: See TracBrowser for help on using the repository browser.