source: branches/1.2/xcache.c @ 377

Last change on this file since 377 was 377, checked in by moo, 7 years ago

merged [374] [375] from trunk->1.2: misses/hits was not updated for var cache

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