source: branches/1.2/xcache.c @ 368

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

use xcache.admin.auth instead of xcache.auth, updated NEWS, ini files

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