source: svn/branches/1.2/xcache.c @ 453

Last change on this file since 453 was 453, checked in by Xuefer, 10 years ago

for non cachable files, they just wont be cached. we shound't scare the users with high 'misses'

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