source: branches/1.2/xcache.c @ 392

Last change on this file since 392 was 392, checked in by moo, 8 years ago

merged 391 from trunk->1.2: fixed #96, wrong format string for sprintf

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