source: branches/1.2/xcache.c @ 358

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

fixed #71 inode hasing issue on some fs, by folding hash value with xor so every bits takes its value

File size: 69.5 KB
RevLine 
[1]1
[205]2#if 0
3#define DEBUG
[236]4#endif
5
6#if 0
[230]7#define SHOW_DPRINT
[205]8#endif
[1]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"
[34]19#include "ext/standard/md5.h"
[82]20#include "ext/standard/php_math.h"
[1]21#include "zend_extensions.h"
22#include "SAPI.h"
23
24#include "xcache.h"
25#include "optimizer.h"
[27]26#include "coverager.h"
[1]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
[18]37#   undef NDEBUG
[1]38#   undef inline
39#   define inline
40#else
[18]41#   ifndef NDEBUG
42#       define NDEBUG
43#   endif
[1]44#endif
45#include <assert.h>
46
[114]47#define VAR_ENTRY_EXPIRED(pentry) ((pentry)->ttl && XG(request_time) > pentry->ctime + (pentry)->ttl)
[1]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)
[130]51
52#define ENTER_LOCK_EX(x) \
[1]53    xc_lock(x->lck); \
54    zend_try { \
55        do
[130]56#define LEAVE_LOCK_EX(x) \
[1]57        while (0); \
58    } zend_catch { \
59        catched = 1; \
60    } zend_end_try(); \
[130]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    } \
[1]71} while(0)
[130]72
[1]73/* }}} */
74
75/* {{{ globals */
[148]76static char *xc_shm_scheme = NULL;
[1]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
[114]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
[1]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
[189]105zend_bool xc_have_op_array_ctor = 0;
106
[1]107static zend_bool xc_module_gotup = 0;
108static zend_bool xc_zend_extension_gotup = 0;
[278]109static zend_bool xc_zend_extension_faked = 0;
[1]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;
[165]132                if (ap->inode) {
133                    return ap->inode == bp->inode
134                        && ap->device == bp->device;
135                }
[1]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/* }}} */
[137]163static void xc_entry_free_real_dmz(volatile xc_entry_t *xce) /* {{{ */
[1]164{
[148]165    xce->cache->mem->handlers->free(xce->cache->mem, (xc_entry_t *)xce);
[1]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;
[32]173    xce->cache->entries_count ++;
[1]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/* }}} */
[137]194static void xc_entry_free_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
[114]195{
196    xce->cache->entries_count --;
197    if (xce->refcount == 0) {
[137]198        xc_entry_free_real_dmz(xce);
[114]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/* }}} */
[1]209static void xc_entry_remove_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
210{
[114]211    xc_entry_t **pp = &(xce->cache->entries[xce->hvalue]);
[1]212    xc_entry_t *p;
[114]213    for (p = *pp; p; pp = &(p->next), p = p->next) {
[1]214        if (xc_entry_equal_dmz(xce, p)) {
[137]215            /* unlink */
216            *pp = p->next;
[138]217            xc_entry_free_dmz(xce TSRMLS_CC);
[1]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{
[140]245#ifdef DEBUG
[297]246    fprintf(stderr, "hold %s\n", xce->name.str.val);
[140]247#endif
[1]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
[114]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]);
[137]271        for (p = *pp; p; p = *pp) {
[114]272            if (apply_func(p TSRMLS_CC)) {
[137]273                /* unlink */
274                *pp = p->next;
[138]275                xc_entry_free_dmz(p TSRMLS_CC);
[114]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:
[120]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
[114]289 */
[120]290static XC_ENTRY_APPLY_FUNC(xc_gc_expires_php_entry_dmz) /* {{{ */
[114]291{
[116]292#ifdef DEBUG
[297]293    fprintf(stderr, "ttl %lu, %lu %lu\n", (zend_ulong) XG(request_time), (zend_ulong) entry->atime, xc_php_ttl);
[116]294#endif
[114]295    if (XG(request_time) > entry->atime + xc_php_ttl) {
296        return 1;
297    }
298    return 0;
299}
300/* }}} */
[120]301static XC_ENTRY_APPLY_FUNC(xc_gc_expires_var_entry_dmz) /* {{{ */
[114]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{
[116]311#ifdef DEBUG
[297]312    fprintf(stderr, "interval %lu, %lu %lu\n", (zend_ulong) XG(request_time), (zend_ulong) cache->last_gc_expires, gc_interval);
[116]313#endif
[129]314    if (XG(request_time) - cache->last_gc_expires >= gc_interval) {
[114]315        ENTER_LOCK(cache) {
[129]316            if (XG(request_time) - cache->last_gc_expires >= gc_interval) {
[114]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/* }}} */
[120]324static void xc_gc_expires_php(TSRMLS_D) /* {{{ */
[114]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 ++) {
[120]333        xc_gc_expires_one(xc_php_caches[i], xc_php_gc_interval, xc_gc_expires_php_entry_dmz TSRMLS_CC);
[114]334    }
335}
336/* }}} */
[120]337static void xc_gc_expires_var(TSRMLS_D) /* {{{ */
[114]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 ++) {
[120]346        xc_gc_expires_one(xc_var_caches[i], xc_var_gc_interval, xc_gc_expires_var_entry_dmz TSRMLS_CC);
[114]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;
[141]356    for (p = *pp; p; p = *pp) {
[114]357        if (XG(request_time) - p->dtime > 3600) {
358            p->refcount = 0;
359            /* issue warning here */
360        }
361        if (p->refcount == 0) {
[137]362            /* unlink */
[114]363            *pp = p->next;
364            cache->deletes_count --;
[137]365            xc_entry_free_real_dmz(p);
[114]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) {
[136]378                cache->last_gc_deletes = XG(request_time);
[114]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
[1]399/* helper functions for user functions */
[118]400static void xc_fillinfo_dmz(int cachetype, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
[1]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;
[148]408    const xc_mem_handlers_t *handlers = mem->handlers;
[129]409    zend_ulong interval = (cachetype == XC_TYPE_PHP) ? xc_php_gc_interval : xc_var_gc_interval;
[1]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
[114]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);
[118]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    }
[1]426
427    MAKE_STD_ZVAL(blocks);
428    array_init(blocks);
429
[148]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));
[1]432    add_assoc_bool_ex(return_value, ZEND_STRS("can_readonly"), xc_readonly_protection);
433
[148]434    for (b = handlers->freeblock_first(mem); b; b = handlers->freeblock_next(b)) {
[1]435        zval *bi;
436
437        MAKE_STD_ZVAL(bi);
438        array_init(bi);
439
[148]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));
[1]442        add_next_index_zval(blocks, bi);
443#ifndef NDEBUG
[148]444        avail += handlers->block_size(b);
[1]445#endif
446    }
447    add_assoc_zval_ex(return_value, ZEND_STRS("free_blocks"), blocks);
[148]448    assert(avail == handlers->avail(mem));
[1]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);
[146]465    if (del) {
466        add_assoc_long_ex(ei, ZEND_STRS("dtime"), entry->dtime);
467    }
[1]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
[96]486    add_assoc_stringl_ex(ei, ZEND_STRS("name"), entry->name.str.val, entry->name.str.len, 1);
[1]487#endif
488    switch (entry->type) {
489        case XC_TYPE_PHP:
490            php = entry->data.php;
[95]491            add_assoc_long_ex(ei, ZEND_STRS("sourcesize"),    php->sourcesize);
[1]492#ifdef HAVE_INODE
[95]493            add_assoc_long_ex(ei, ZEND_STRS("device"),        php->device);
494            add_assoc_long_ex(ei, ZEND_STRS("inode"),         php->inode);
[1]495#endif
[95]496            add_assoc_long_ex(ei, ZEND_STRS("mtime"),         php->mtime);
[1]497
[95]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);
[268]503#ifdef ZEND_ENGINE_2_1
504            add_assoc_long_ex(ei, ZEND_STRS("autoglobal_cnt"),php->autoglobal_cnt);
505#endif
[1]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) {
[11]529            xc_fillentry_dmz(e, 0, list TSRMLS_CC);
[1]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) {
[11]537        xc_fillentry_dmz(e, 1, list TSRMLS_CC);
[1]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
[212]552    CG(active_op_array) = p->op_array;
553
[95]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
[1]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) {
[224]577            zend_uint class_idx = (/* class_num */ (int) (long) ce->parent) - 1;
[1]578            assert(class_idx < i);
579            ci->cest.parent = new_cest_ptrs[class_idx];
580        }
581        new_cest_ptrs[i] =
582#endif
[212]583        xc_install_class(xce->name.str.val, &ci->cest, ci->oplineno,
[1]584                UNISW(0, ci->type), ci->key, ci->key_size TSRMLS_CC);
585    }
586
[268]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
[1]603    i = 1;
604    zend_hash_add(&EG(included_files), xce->name.str.val, xce->name.str.len+1, (void *)&i, sizeof(int), NULL);
[131]605    if (h) {
606        zend_llist_add_element(&CG(open_files), h);
607    }
[1]608
609#ifndef ZEND_ENGINE_2
610    free_alloca(new_cest_ptrs);
611#endif
612    return p->op_array;
613}
614/* }}} */
615
[11]616static inline void xc_entry_unholds_real(xc_stack_t *holds, xc_cache_t **caches, int cachecount TSRMLS_DC) /* {{{ */
[1]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];
[140]625#ifdef DEBUG
626        fprintf(stderr, "holded %d\n", xc_stack_size(s));
627#endif
[1]628        if (xc_stack_size(s)) {
629            cache = ((xc_entry_t *)xc_stack_top(s))->cache;
630            ENTER_LOCK(cache) {
[140]631                while (xc_stack_size(s)) {
632                    xce = (xc_entry_t*) xc_stack_pop(s);
633#ifdef DEBUG
[297]634                    fprintf(stderr, "unhold %s\n", xce->name.str.val);
[140]635#endif
[1]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{
[11]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);
[1]648}
649/* }}} */
[11]650static int xc_stat(const char *filename, const char *include_path, struct stat *pbuf TSRMLS_DC) /* {{{ */
[1]651{
[165]652    char filepath[MAXPATHLEN];
[1]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
[11]661    for (path = php_strtok_r(paths, tokens, &tokbuf); path; path = php_strtok_r(NULL, tokens, &tokbuf)) {
[165]662        if (snprintf(filepath, sizeof(filepath), "%s/%s", path, filename) >= MAXPATHLEN - 1) {
[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)
[358]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/* }}} */
[165]692static inline xc_hash_value_t xc_entry_hash_name(xc_entry_t *xce TSRMLS_DC) /* {{{ */
[1]693{
[103]694    return UNISW(NOTHING, UG(unicode) ? HASH_USTR_L(xce->name_type, xce->name.uni.val, xce->name.uni.len) :)
[1]695        HASH_STR_L(xce->name.str.val, xce->name.str.len);
696}
697/* }}} */
[165]698#define xc_entry_hash_var xc_entry_hash_name
[152]699static inline xc_hash_value_t xc_entry_hash_php(xc_entry_t *xce TSRMLS_DC) /* {{{ */
[1]700{
701#ifdef HAVE_INODE
[165]702    if (xce->data.php->inode) {
703        return HASH(xce->data.php->device + xce->data.php->inode);
704    }
[1]705#endif
[165]706    return xc_entry_hash_name(xce TSRMLS_CC);
[1]707}
708/* }}} */
[165]709static int xc_entry_init_key_php(xc_entry_t *xce, char *filename, char *opened_path_buffer TSRMLS_DC) /* {{{ */
[1]710{
711    struct stat buf, *pbuf;
712    xc_hash_value_t hv;
713    int cacheid;
714    xc_entry_data_php_t *php;
[75]715    char *ptr;
[1]716
717    if (!filename || !SG(request_info).path_translated) {
718        return 0;
719    }
720
[165]721    php = xce->data.php;
722
723    if (XG(stat)) {
[1]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) {
[165]728                goto stat_done;
[1]729            }
730        }
731
[75]732        /* absolute path */
[1]733        pbuf = &buf;
734        if (IS_ABSOLUTE_PATH(filename, strlen(filename))) {
735            if (VCWD_STAT(filename, pbuf) != 0) {
736                return 0;
737            }
[165]738            goto stat_done;
[1]739        }
[75]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) {
[1]752                return 0;
753            }
[165]754            goto stat_done;
[1]755        }
[75]756not_relative_path:
757
758        /* use include_path */
759        if (xc_stat(filename, PG(include_path), pbuf TSRMLS_CC) != 0) {   
760            return 0;
761        }
[1]762
[165]763        /* fall */
764
765stat_done:
[202]766        if (XG(request_time) - pbuf->st_mtime < 2 && !xc_test) {
[165]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;
[1]776    }
[165]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    }
[1]785
[165]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
[19]797    UNISW(NOTHING, xce->name_type = IS_STRING;)
[1]798    xce->name.str.val = filename;
799    xce->name.str.len = strlen(filename);
800
[152]801    hv = xc_entry_hash_php(xce TSRMLS_CC);
[358]802    cacheid = xc_hash_fold(hv, &xc_php_hcache);
[1]803    xce->cache = xc_php_caches[cacheid];
[358]804    xce->hvalue = xc_hash_fold(hv, &xc_php_hentry);
[1]805
806    xce->type = XC_TYPE_PHP;
807    return 1;
808}
809/* }}} */
[212]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/* }}} */
[1]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;
[165]849    char opened_path_buffer[MAXPATHLEN];
[95]850    int old_constinfo_cnt, old_funcinfo_cnt, old_classinfo_cnt;
[212]851    int i;
[1]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;
[165]876    if (!xc_entry_init_key_php(&xce, filename, opened_path_buffer TSRMLS_CC)) {
[1]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;
[130]890    ENTER_LOCK_EX(cache) {
[1]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 ++;
[130]912    } LEAVE_LOCK_EX(cache);
[1]913
[130]914    if (catched) {
915        cache->compiling = 0;
916        zend_bailout();
917    }
918
[1]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
[95]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
[1]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
[86]956    filename = h->opened_path ? h->opened_path : h->filename;
[165]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        }
[86]967    }
[212]968#endif
[86]969
[1]970#ifdef HAVE_XCACHE_OPTIMIZER
971    if (XG(optimizer)) {
972        xc_optimize(op_array TSRMLS_CC);
973    }
974#endif
[212]975    /* }}} */
976    /* {{{ prepare */
[1]977    php.op_array      = op_array;
978
[95]979#ifdef HAVE_XCACHE_CONSTANT
[268]980    php.constinfo_cnt  = zend_hash_num_elements(EG(zend_constants)) - old_constinfo_cnt;
[95]981#endif
[268]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;
[1]987
[268]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
[95]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
[268]1013    X_ALLOC_N(constinfos,  constinfo_cnt);
[95]1014#endif
[268]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
[95]1020#undef X_ALLOC
[1]1021    /* }}} */
1022    /* {{{ shallow copy, pointers only */ {
1023        Bucket *b;
1024        unsigned int i;
1025
[95]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;)             \
[103]1038        if (UNISW(1, b->key.type == IS_STRING)) {             \
[200]1039            ZSTR_S(data->key)      = BUCKET_KEY_S(b);          \
[103]1040        }                                                     \
1041        else {                                                \
[200]1042            ZSTR_U(data->key)      = BUCKET_KEY_U(b);         \
[103]1043        }                                                     \
[95]1044        data->key_size   = b->nKeyLength;                     \
1045    }                                                         \
1046} while(0)
[1]1047
[95]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);
[1]1053
[95]1054#undef COPY_H
[268]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) {
[299]1065                xc_autoglobal_t *data = &php.autoglobals[i];
[268]1066
1067                assert(i < php.autoglobal_cnt);
[299]1068                i ++;
[268]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
[1]1080    }
1081    /* }}} */
[212]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    /* }}} */
[230]1092#ifdef SHOW_DPRINT
1093    xc_dprint(&xce, 0 TSRMLS_CC);
1094#endif
[130]1095    ENTER_LOCK_EX(cache) { /* {{{ store/add entry */
[1]1096        stored_xce = xc_entry_store_dmz(&xce TSRMLS_CC);
[130]1097    } LEAVE_LOCK_EX(cache);
[1]1098    /* }}} */
1099#ifdef DEBUG
1100    fprintf(stderr, "stored\n");
1101#endif
1102
[95]1103#define X_FREE(var) \
1104    if (xce.data.php->var) { \
1105        efree(xce.data.php->var); \
1106    } \
1107err_##var:
1108
[268]1109#ifdef ZEND_ENGINE_2_1
1110    X_FREE(autoglobals)
1111#endif
[95]1112    X_FREE(classinfos)
1113    X_FREE(funcinfos)
1114#ifdef HAVE_XCACHE_CONSTANT
1115    X_FREE(constinfos)
1116#endif
1117#undef X_FREE
1118
[1]1119err_oparray:
1120err_bailout:
1121
1122    if (xc_test && stored_xce) {
[131]1123        /* free it, no install. restore now */
[1]1124        xc_sandbox_free(&sandbox, 0 TSRMLS_CC);
1125    }
[216]1126    else if (!op_array) {
1127        /* failed to compile free it, no install */
1128        xc_sandbox_free(&sandbox, 0 TSRMLS_CC);
1129    }
[1]1130    else {
[212]1131        CG(active_op_array) = op_array;
[1]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) {
[132]1142#ifdef ZEND_ENGINE_2
[119]1143        destroy_op_array(op_array TSRMLS_CC);
[132]1144#else
1145        destroy_op_array(op_array);
1146#endif
[119]1147        efree(op_array);
[131]1148        h = NULL;
[1]1149        goto restore;
1150    }
1151    return op_array;
1152
1153restore:
[235]1154    CG(in_compilation)    = 1;
1155    CG(compiled_filename) = stored_xce->name.str.val;
1156    CG(zend_lineno)       = 0;
[1]1157#ifdef DEBUG
[236]1158    fprintf(stderr, "restoring %s\n", stored_xce->name.str.val);
[1]1159#endif
1160    xc_processor_restore_xc_entry_t(&xce, stored_xce, xc_readonly_protection TSRMLS_CC);
[230]1161#ifdef SHOW_DPRINT
1162    xc_dprint(&xce, 0 TSRMLS_CC);
1163#endif
[1]1164
[95]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    }
[268]1176#ifdef ZEND_ENGINE_2_1
1177    X_FREE(autoglobals)
1178#endif
[95]1179    X_FREE(classinfos)
1180    X_FREE(funcinfos)
1181#ifdef HAVE_XCACHE_CONSTANT
1182    X_FREE(constinfos)
1183#endif
1184#undef X_FREE
[1]1185    efree(xce.data.php);
[95]1186
1187    if (catched) {
1188        zend_bailout();
1189    }
[235]1190    CG(in_compilation)    = 0;
1191    CG(compiled_filename) = NULL;
[1]1192#ifdef DEBUG
[236]1193    fprintf(stderr, "restored  %s\n", stored_xce->name.str.val);
[1]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{
[148]1202    xc_shm_t *shm;
[1]1203    int i;
1204    if (!xc_initized) {
1205        return 0;
1206    }
1207    for (i = 0; i < xc_php_hcache.size; i ++) {
[148]1208        shm = xc_php_caches[i]->shm;
1209        if (shm->handlers->is_readwrite(shm, p)) {
[1]1210            return 1;
1211        }
1212    }
1213    for (i = 0; i < xc_var_hcache.size; i ++) {
[148]1214        shm = xc_var_caches[i]->shm;
1215        if (shm->handlers->is_readwrite(shm, p)) {
[1]1216            return 1;
1217        }
1218    }
1219    return 0;
1220}
1221/* }}} */
1222int xc_is_ro(const void *p) /* {{{ */
1223{
[148]1224    xc_shm_t *shm;
[1]1225    int i;
1226    if (!xc_initized) {
1227        return 0;
1228    }
1229    for (i = 0; i < xc_php_hcache.size; i ++) {
[148]1230        shm = xc_php_caches[i]->shm;
1231        if (shm->handlers->is_readonly(shm, p)) {
[1]1232            return 1;
1233        }
1234    }
1235    for (i = 0; i < xc_var_hcache.size; i ++) {
[148]1236        shm = xc_var_caches[i]->shm;
1237        if (shm->handlers->is_readonly(shm, p)) {
[1]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{
[11]1253    typedef struct {
[1]1254        const char *prefix;
[20]1255        zend_uchar (*getsize)();
[1]1256        const char *(*get)(zend_uchar i);
[11]1257    } xc_meminfo_t;
1258    xc_meminfo_t nameinfos[] = {
[1]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    };
[11]1265    xc_meminfo_t* p;
[20]1266    zend_uchar i, count;
[1]1267    char const_name[96];
1268    int const_name_len;
1269    int undefdone = 0;
1270
1271    for (p = nameinfos; p->getsize; p ++) {
[20]1272        count = p->getsize();
1273        for (i = 0; i < count; i ++) {
[1]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);
[119]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);
[1]1290    return 0;
1291}
1292/* }}} */
[11]1293static xc_shm_t *xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache) /* {{{ */
[1]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) {
[148]1311                cache->mem->handlers->free(cache->mem, cache->entries);
[1]1312            }
[148]1313            cache->mem->handlers->free(cache->mem, cache);
[1]1314            */
[154]1315            shm = cache->shm;
[148]1316            shm->handlers->memdestroy(cache->mem);
[1]1317        }
1318    }
1319    free(caches);
1320    return shm;
1321}
1322/* }}} */
[148]1323static xc_cache_t **xc_cache_init(xc_shm_t *shm, xc_hash_t *hcache, xc_hash_t *hentry, xc_shmsize_t shmsize) /* {{{ */
[1]1324{
1325    xc_cache_t **caches = NULL, *cache;
1326    xc_mem_t *mem;
[114]1327    time_t now = time(NULL);
[1]1328    int i;
[49]1329    xc_memsize_t memsize;
[11]1330
[49]1331    memsize = shmsize / hcache->size;
[1]1332
[49]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
[1]1344    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
1345
1346    for (i = 0; i < hcache->size; i ++) {
[148]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");
[1]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;
[114]1357        cache->last_gc_deletes = now;
1358        cache->last_gc_expires = now;
[1]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    }
[334]1383    xc_php_hcache.size = 0;
1384
[1]1385    if (xc_var_caches) {
1386        shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
1387        xc_var_caches = NULL;
1388    }
[334]1389    xc_var_hcache.size = 0;
1390
[1]1391    if (shm) {
1392        xc_shm_destroy(shm);
1393    }
[334]1394
1395    xc_initized = 0;
[1]1396}
1397/* }}} */
1398static int xc_init(int module_number TSRMLS_DC) /* {{{ */
1399{
1400    xc_shm_t *shm;
[334]1401    xc_shmsize_t shmsize = ALIGN(xc_php_size) + ALIGN(xc_var_size);
[1]1402
[11]1403    xc_php_caches = xc_var_caches = NULL;
[334]1404    shm = NULL;
[11]1405
[334]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
[1]1411    if (xc_php_size || xc_var_size) {
[334]1412        CHECK(shm = xc_shm_init(xc_shm_scheme, shmsize, xc_readonly_protection, xc_mmap_path, NULL), "Cannot create shm");
[148]1413        if (!shm->handlers->can_readonly(shm)) {
[1]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
[148]1421            CHECK(xc_php_caches = xc_cache_init(shm, &xc_php_hcache, &xc_php_hentry, xc_php_size), "failed init opcode cache");
[1]1422        }
1423
1424        if (xc_var_size) {
[148]1425            CHECK(xc_var_caches = xc_cache_init(shm, &xc_var_hcache, &xc_var_hentry, xc_var_size), "failed init variable cache");
[1]1426        }
1427    }
[334]1428    return SUCCESS;
[1]1429
1430err:
[334]1431    xc_destroy();
[1]1432    if (xc_php_caches || xc_var_caches) {
[334]1433        /* shm destroied in xc_destroy() */
[1]1434    }
1435    else if (shm) {
1436        xc_shm_destroy(shm);
1437    }
1438    return 0;
1439}
1440/* }}} */
1441static void xc_request_init(TSRMLS_D) /* {{{ */
1442{
[17]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
[1]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    }
[27]1466#ifdef HAVE_XCACHE_COVERAGER
1467    xc_coverager_request_init(TSRMLS_C);
[1]1468#endif
1469}
1470/* }}} */
1471static void xc_request_shutdown(TSRMLS_D) /* {{{ */
1472{
1473    xc_entry_unholds(TSRMLS_C);
[120]1474    xc_gc_expires_php(TSRMLS_C);
1475    xc_gc_expires_var(TSRMLS_C);
[114]1476    xc_gc_deletes(TSRMLS_C);
[27]1477#ifdef HAVE_XCACHE_COVERAGER
1478    xc_coverager_request_shutdown(TSRMLS_C);
[1]1479#endif
1480}
1481/* }}} */
[92]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
[1]1489{
[92]1490    memset(xcache_globals, 0, sizeof(zend_xcache_globals));
[1]1491}
1492/* }}} */
[92]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
[1]1500{
1501    int i;
1502
[92]1503    if (xcache_globals->php_holds != NULL) {
[1]1504        for (i = 0; i < xc_php_hcache.size; i ++) {
[92]1505            xc_stack_destroy(&xcache_globals->php_holds[i]);
[1]1506        }
[92]1507        free(xcache_globals->php_holds);
1508        xcache_globals->php_holds = NULL;
[1]1509    }
1510
[92]1511    if (xcache_globals->var_holds != NULL) {
[1]1512        for (i = 0; i < xc_var_hcache.size; i ++) {
[92]1513            xc_stack_destroy(&xcache_globals->var_holds[i]);
[1]1514        }
[92]1515        free(xcache_globals->var_holds);
1516        xcache_globals->var_holds = NULL;
[1]1517    }
1518}
1519/* }}} */
1520
1521/* user functions */
[55]1522static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
[34]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    if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
1532        admin_user = NULL;
1533    }
1534    if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
1535        admin_pass = NULL;
1536    }
1537
1538    if (admin_user == NULL || admin_pass == NULL) {
1539        php_error_docref(NULL TSRMLS_CC, E_ERROR, "xcache.admin.user and xcache.admin.pass is required");
1540        zend_bailout();
1541    }
1542    if (strlen(admin_pass) != 32) {
1543        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));
1544        zend_bailout();
1545    }
1546
[105]1547#ifdef ZEND_ENGINE_2_1
[113]1548    zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
[105]1549#endif
[34]1550    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
1551        php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
1552        zend_bailout();
1553    }
1554    ht = HASH_OF((*server));
1555
1556    if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
1557        user = NULL;
1558    }
1559    else if (Z_TYPE_PP(user) != IS_STRING) {
1560        user = NULL;
1561    }
1562
1563    if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
1564        pass = NULL;
1565    }
1566    else if (Z_TYPE_PP(pass) != IS_STRING) {
1567        pass = NULL;
1568    }
1569
1570    if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
1571        PHP_MD5_CTX context;
1572        char md5str[33];
1573        unsigned char digest[16];
1574
1575        PHP_MD5Init(&context);
[91]1576        PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
[34]1577        PHP_MD5Final(digest, &context);
1578
1579        md5str[0] = '\0';
1580        make_digest(md5str, digest);
1581        if (strcmp(admin_pass, md5str) == 0) {
1582            return 1;
1583        }
1584    }
1585
1586#define STR "WWW-authenticate: basic realm='XCache Administration'"
1587    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1588#undef STR
1589#define STR "HTTP/1.0 401 Unauthorized"
1590    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1591#undef STR
1592    ZEND_PUTS("XCache Auth Failed. User and Password is case sense\n");
1593
1594    zend_bailout();
1595    return 0;
1596}
1597/* }}} */
1598/* {{{ xcache_admin_operate */
[1]1599typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
[34]1600static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
[1]1601{
1602    long type;
1603    int size;
1604    xc_cache_t **caches, *cache;
1605    long id = 0;
1606
[279]1607    xcache_admin_auth_check(TSRMLS_C);
1608
[11]1609    if (!xc_initized) {
[279]1610        RETURN_NULL();
[11]1611    }
1612
[1]1613    if (optype == XC_OP_COUNT) {
1614        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
1615            return;
1616        }
1617    }
1618    else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
1619        return;
1620    }
1621
1622    switch (type) {
1623        case XC_TYPE_PHP:
1624            size = xc_php_hcache.size;
1625            caches = xc_php_caches;
1626            break;
1627
1628        case XC_TYPE_VAR:
1629            size = xc_var_hcache.size;
1630            caches = xc_var_caches;
1631            break;
1632
1633        default:
1634            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
1635            RETURN_FALSE;
1636    }
1637
1638    switch (optype) {
1639        case XC_OP_COUNT:
1640            RETURN_LONG(size)
1641            break;
1642
1643        case XC_OP_INFO:
1644        case XC_OP_LIST:
1645            if (id < 0 || id >= size) {
1646                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1647                RETURN_FALSE;
1648            }
1649
1650            array_init(return_value);
1651
1652            cache = caches[id];
1653            ENTER_LOCK(cache) {
1654                if (optype == XC_OP_INFO) {
[118]1655                    xc_fillinfo_dmz(type, cache, return_value TSRMLS_CC);
[1]1656                }
1657                else {
1658                    xc_filllist_dmz(cache, return_value TSRMLS_CC);
1659                }
1660            } LEAVE_LOCK(cache);
1661            break;
1662        case XC_OP_CLEAR:
1663            {
[141]1664                xc_entry_t *e, *next;
[1]1665                int i, c;
1666
1667                if (id < 0 || id >= size) {
1668                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1669                    RETURN_FALSE;
1670                }
1671
1672                cache = caches[id];
1673                ENTER_LOCK(cache) {
1674                    for (i = 0, c = cache->hentry->size; i < c; i ++) {
[141]1675                        for (e = cache->entries[i]; e; e = next) {
1676                            next = e->next;
[1]1677                            xc_entry_remove_dmz(e TSRMLS_CC);
1678                        }
1679                        cache->entries[i] = NULL;
1680                    }
1681                } LEAVE_LOCK(cache);
[114]1682                xc_gc_deletes(TSRMLS_C);
[1]1683            }
1684            break;
1685
1686        default:
1687            assert(0);
1688    }
1689}
1690/* }}} */
[9]1691/* {{{ proto int xcache_count(int type)
1692   Return count of cache on specified cache type */
[1]1693PHP_FUNCTION(xcache_count)
1694{
[34]1695    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
[1]1696}
1697/* }}} */
[9]1698/* {{{ proto array xcache_info(int type, int id)
1699   Get cache info by id on specified cache type */
[1]1700PHP_FUNCTION(xcache_info)
1701{
[34]1702    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
[1]1703}
1704/* }}} */
[9]1705/* {{{ proto array xcache_list(int type, int id)
1706   Get cache entries list by id on specified cache type */
[1]1707PHP_FUNCTION(xcache_list)
1708{
[34]1709    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
[1]1710}
1711/* }}} */
[9]1712/* {{{ proto array xcache_clear_cache(int type, int id)
1713   Clear cache by id on specified cache type */
[1]1714PHP_FUNCTION(xcache_clear_cache)
1715{
[34]1716    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
[1]1717}
1718/* }}} */
1719
[328]1720#define VAR_DISABLED_WARNING() do { \
1721        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.var_size is either 0 or too small to enable var data caching"); \
1722} while (0)
1723
[1]1724static int xc_entry_init_key_var(xc_entry_t *xce, zval *name TSRMLS_DC) /* {{{ */
1725{
1726    xc_hash_value_t hv;
1727    int cacheid;
1728
1729    switch (Z_TYPE_P(name)) {
1730#ifdef IS_UNICODE
1731        case IS_UNICODE:
1732#endif
1733        case IS_STRING:
1734            break;
1735        default:
1736#ifdef IS_UNICODE
1737            convert_to_text(name);
1738#else
1739            convert_to_string(name);
1740#endif
1741    }
1742#ifdef IS_UNICODE
1743    xce->name_type = name->type;
1744#endif
1745    xce->name = name->value;
1746
[152]1747    hv = xc_entry_hash_var(xce TSRMLS_CC);
[1]1748
1749    cacheid = (hv & xc_var_hcache.mask);
1750    xce->cache = xc_var_caches[cacheid];
1751    hv >>= xc_var_hcache.bits;
1752    xce->hvalue = (hv & xc_var_hentry.mask);
1753
1754    xce->type = XC_TYPE_VAR;
1755    return SUCCESS;
1756}
1757/* }}} */
[9]1758/* {{{ proto mixed xcache_get(string name)
1759   Get cached data by specified name */
[1]1760PHP_FUNCTION(xcache_get)
1761{
1762    xc_entry_t xce, *stored_xce;
1763    xc_entry_data_var_t var;
1764    zval *name;
1765
[328]1766    if (!xc_var_caches) {
1767        VAR_DISABLED_WARNING();
1768        RETURN_NULL();
1769    }
1770
[1]1771    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1772        return;
1773    }
1774    xce.data.var = &var;
1775    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1776
1777    ENTER_LOCK(xce.cache) {
1778        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1779        if (stored_xce) {
[254]1780            if (!VAR_ENTRY_EXPIRED(stored_xce)) {
[233]1781                xc_processor_restore_zval(return_value, stored_xce->data.var->value, stored_xce->have_references TSRMLS_CC);
[1]1782                /* return */
1783                break;
1784            }
1785            else {
[11]1786                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]1787            }
1788        }
1789
1790        RETVAL_NULL();
1791    } LEAVE_LOCK(xce.cache);
1792}
1793/* }}} */
[9]1794/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
1795   Store data to cache by specified name */
[1]1796PHP_FUNCTION(xcache_set)
1797{
1798    xc_entry_t xce, *stored_xce;
1799    xc_entry_data_var_t var;
1800    zval *name;
1801    zval *value;
1802
[328]1803    if (!xc_var_caches) {
1804        VAR_DISABLED_WARNING();
1805        RETURN_NULL();
1806    }
1807
[114]1808    xce.ttl = XG(var_ttl);
1809    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &xce.ttl) == FAILURE) {
[1]1810        return;
1811    }
[114]1812
1813    /* max ttl */
1814    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
1815        xce.ttl = xc_var_maxttl;
1816    }
1817
[1]1818    xce.data.var = &var;
1819    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1820
1821    ENTER_LOCK(xce.cache) {
1822        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1823        if (stored_xce) {
[11]1824            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]1825        }
1826        var.value = value;
[11]1827        RETVAL_BOOL(xc_entry_store_dmz(&xce TSRMLS_CC) != NULL ? 1 : 0);
[1]1828    } LEAVE_LOCK(xce.cache);
1829}
1830/* }}} */
[9]1831/* {{{ proto bool  xcache_isset(string name)
1832   Check if an entry exists in cache by specified name */
[1]1833PHP_FUNCTION(xcache_isset)
1834{
1835    xc_entry_t xce, *stored_xce;
1836    xc_entry_data_var_t var;
1837    zval *name;
1838
[328]1839    if (!xc_var_caches) {
1840        VAR_DISABLED_WARNING();
1841        RETURN_FALSE;
1842    }
1843
[1]1844    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1845        return;
1846    }
1847    xce.data.var = &var;
1848    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1849
1850    ENTER_LOCK(xce.cache) {
1851        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1852        if (stored_xce) {
[126]1853            if (!VAR_ENTRY_EXPIRED(stored_xce)) {
[1]1854                RETVAL_TRUE;
1855                /* return */
1856                break;
1857            }
1858            else {
[11]1859                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]1860            }
1861        }
1862
1863        RETVAL_FALSE;
1864    } LEAVE_LOCK(xce.cache);
1865}
1866/* }}} */
[9]1867/* {{{ proto bool  xcache_unset(string name)
1868   Unset existing data in cache by specified name */
[1]1869PHP_FUNCTION(xcache_unset)
1870{
1871    xc_entry_t xce, *stored_xce;
1872    xc_entry_data_var_t var;
1873    zval *name;
1874
[328]1875    if (!xc_var_caches) {
1876        VAR_DISABLED_WARNING();
1877        RETURN_FALSE;
1878    }
1879
[1]1880    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1881        return;
1882    }
1883    xce.data.var = &var;
1884    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1885
1886    ENTER_LOCK(xce.cache) {
1887        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1888        if (stored_xce) {
[11]1889            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]1890            RETVAL_TRUE;
1891        }
1892        else {
1893            RETVAL_FALSE;
1894        }
1895    } LEAVE_LOCK(xce.cache);
1896}
1897/* }}} */
1898static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
1899{
1900    xc_entry_t xce, *stored_xce;
1901    xc_entry_data_var_t var, *stored_var;
1902    zval *name;
1903    long count = 1;
1904    long value = 0;
1905    zval oldzval;
1906
[328]1907    if (!xc_var_caches) {
1908        VAR_DISABLED_WARNING();
1909        RETURN_NULL();
1910    }
1911
[114]1912    xce.ttl = XG(var_ttl);
1913    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &xce.ttl) == FAILURE) {
[1]1914        return;
1915    }
[114]1916
1917    /* max ttl */
1918    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
1919        xce.ttl = xc_var_maxttl;
1920    }
1921
[1]1922    xce.data.var = &var;
1923    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1924
1925    ENTER_LOCK(xce.cache) {
1926        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1927        if (stored_xce) {
1928#ifdef DEBUG
1929            fprintf(stderr, "incdec: gotxce %s\n", xce.name.str.val);
1930#endif
1931            /* timeout */
[114]1932            if (VAR_ENTRY_EXPIRED(stored_xce)) {
[1]1933#ifdef DEBUG
1934                fprintf(stderr, "incdec: expired\n");
1935#endif
[11]1936                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]1937                stored_xce = NULL;
1938            }
1939            else {
1940                /* do it in place */
[114]1941                stored_var = stored_xce->data.var;
[1]1942                if (Z_TYPE_P(stored_var->value) == IS_LONG) {
[114]1943                    stored_xce->ctime = XG(request_time);
1944                    stored_xce->ttl   = xce.ttl;
[1]1945#ifdef DEBUG
1946                    fprintf(stderr, "incdec: islong\n");
1947#endif
1948                    value = Z_LVAL_P(stored_var->value);
1949                    value += (inc == 1 ? count : - count);
1950                    RETVAL_LONG(value);
1951                    Z_LVAL_P(stored_var->value) = value;
[114]1952                    break; /* leave lock */
[1]1953                }
1954                else {
1955#ifdef DEBUG
1956                    fprintf(stderr, "incdec: notlong\n");
1957#endif
[233]1958                    xc_processor_restore_zval(&oldzval, stored_xce->data.var->value, stored_xce->have_references TSRMLS_CC);
[1]1959                    convert_to_long(&oldzval);
1960                    value = Z_LVAL(oldzval);
1961                    zval_dtor(&oldzval);
1962                }
1963            }
1964        }
1965#ifdef DEBUG
1966        else {
1967            fprintf(stderr, "incdec: %s not found\n", xce.name.str.val);
1968        }
1969#endif
1970
1971        value += (inc == 1 ? count : - count);
1972        RETVAL_LONG(value);
1973        var.value = return_value;
[114]1974
[1]1975        if (stored_xce) {
1976            xce.atime = stored_xce->atime;
1977            xce.ctime = stored_xce->ctime;
1978            xce.hits  = stored_xce->hits;
[11]1979            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]1980        }
[11]1981        xc_entry_store_dmz(&xce TSRMLS_CC);
[1]1982
1983    } LEAVE_LOCK(xce.cache);
1984}
1985/* }}} */
[9]1986/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
1987   Increase an int counter in cache by specified name, create it if not exists */
[1]1988PHP_FUNCTION(xcache_inc)
1989{
1990    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1991}
1992/* }}} */
[9]1993/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
1994   Decrease an int counter in cache by specified name, create it if not exists */
[1]1995PHP_FUNCTION(xcache_dec)
1996{
1997    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1998}
1999/* }}} */
[9]2000/* {{{ proto string xcache_asm(string filename)
2001 */
[1]2002#ifdef HAVE_XCACHE_ASSEMBLER
2003PHP_FUNCTION(xcache_asm)
2004{
2005}
2006#endif
2007/* }}} */
2008#ifdef HAVE_XCACHE_DISASSEMBLER
[9]2009/* {{{ proto array  xcache_dasm_file(string filename)
2010   Disassemble file into opcode array by filename */
[1]2011PHP_FUNCTION(xcache_dasm_file)
2012{
2013    char *filename;
[143]2014    int filename_len;
[1]2015
2016    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
2017        return;
2018    }
2019    if (!filename_len) RETURN_FALSE;
2020
2021    xc_dasm_file(return_value, filename TSRMLS_CC);
2022}
2023/* }}} */
[9]2024/* {{{ proto array  xcache_dasm_string(string code)
2025   Disassemble php code into opcode array */
[1]2026PHP_FUNCTION(xcache_dasm_string)
2027{
2028    zval *code;
2029
2030    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
2031        return;
2032    }
2033    xc_dasm_string(return_value, code TSRMLS_CC);
2034}
2035/* }}} */
2036#endif
[9]2037/* {{{ proto string xcache_encode(string filename)
2038   Encode php file into XCache opcode encoded format */
[1]2039#ifdef HAVE_XCACHE_ENCODER
2040PHP_FUNCTION(xcache_encode)
2041{
2042}
2043#endif
2044/* }}} */
[9]2045/* {{{ proto bool xcache_decode_file(string filename)
2046   Decode(load) opcode from XCache encoded format file */
[1]2047#ifdef HAVE_XCACHE_DECODER
[9]2048PHP_FUNCTION(xcache_decode_file)
[1]2049{
2050}
2051#endif
2052/* }}} */
[9]2053/* {{{ proto bool xcache_decode_string(string data)
2054   Decode(load) opcode from XCache encoded format data */
2055#ifdef HAVE_XCACHE_DECODER
2056PHP_FUNCTION(xcache_decode_string)
2057{
2058}
2059#endif
2060/* }}} */
[1]2061/* {{{ xc_call_getter */
2062typedef const char *(xc_name_getter_t)(zend_uchar type);
2063static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
2064{
2065    long spec;
2066    const char *name;
2067
2068    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
2069        return;
2070    }
2071    if (spec >= 0 && spec < count) {
[11]2072        name = getter((zend_uchar) spec);
[1]2073        if (name) {
2074            /* RETURN_STRING */
2075            int len = strlen(name);
2076            return_value->value.str.len = len;
2077            return_value->value.str.val = estrndup(name, len);
2078            return_value->type = IS_STRING; 
2079            return;
2080        }
2081    }
2082    RETURN_NULL();
2083}
2084/* }}} */
2085/* {{{ proto string xcache_get_op_type(int op_type) */
2086PHP_FUNCTION(xcache_get_op_type)
2087{
2088    xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2089}
2090/* }}} */
2091/* {{{ proto string xcache_get_data_type(int type) */
2092PHP_FUNCTION(xcache_get_data_type)
2093{
2094    xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2095}
2096/* }}} */
2097/* {{{ proto string xcache_get_opcode(int opcode) */
2098PHP_FUNCTION(xcache_get_opcode)
2099{
2100    xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2101}
2102/* }}} */
2103/* {{{ proto string xcache_get_op_spec(int op_type) */
2104PHP_FUNCTION(xcache_get_op_spec)
2105{
2106    xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2107}
2108/* }}} */
[8]2109#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
[1]2110/* {{{ proto string xcache_get_opcode_spec(int opcode) */
2111PHP_FUNCTION(xcache_get_opcode_spec)
2112{
2113    long spec;
2114    const xc_opcode_spec_t *opspec;
2115
2116    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
2117        return;
2118    }
[17]2119    if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
[11]2120        opspec = xc_get_opcode_spec((zend_uchar) spec);
[1]2121        if (opspec) {
2122            array_init(return_value);
2123            add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
2124            add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
2125            add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
2126            add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
2127            return;
2128        }
2129    }
2130    RETURN_NULL();
2131}
2132/* }}} */
[8]2133#endif
[1]2134/* {{{ proto mixed xcache_get_special_value(zval value) */
2135PHP_FUNCTION(xcache_get_special_value)
2136{
2137    zval *value;
2138
2139    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
2140        return;
2141    }
2142
2143    if (value->type == IS_CONSTANT) {
2144        *return_value = *value;
2145        zval_copy_ctor(return_value);
2146        return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
2147        return;
2148    }
2149
2150    if (value->type == IS_CONSTANT_ARRAY) {
2151        *return_value = *value;
2152        zval_copy_ctor(return_value);
2153        return_value->type = IS_ARRAY;
2154        return;
2155    }
2156
2157    RETURN_NULL();
2158}
2159/* }}} */
2160/* {{{ proto string xcache_coredump(int op_type) */
2161PHP_FUNCTION(xcache_coredump)
2162{
[9]2163    if (xc_test) {
2164        raise(SIGSEGV);
2165    }
2166    else {
2167        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
2168    }
[1]2169}
2170/* }}} */
2171/* {{{ proto string xcache_is_autoglobal(string name) */
2172PHP_FUNCTION(xcache_is_autoglobal)
2173{
2174    char *name;
[143]2175    int name_len;
[1]2176
2177    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
2178        return;
2179    }
2180
2181    RETURN_BOOL(zend_hash_exists(CG(auto_globals), name, name_len + 1));
2182}
2183/* }}} */
2184static function_entry xcache_functions[] = /* {{{ */
2185{
2186    PHP_FE(xcache_count,             NULL)
2187    PHP_FE(xcache_info,              NULL)
2188    PHP_FE(xcache_list,              NULL)
2189    PHP_FE(xcache_clear_cache,       NULL)
2190    PHP_FE(xcache_coredump,          NULL)
2191#ifdef HAVE_XCACHE_ASSEMBLER
2192    PHP_FE(xcache_asm,               NULL)
2193#endif
2194#ifdef HAVE_XCACHE_DISASSEMBLER
2195    PHP_FE(xcache_dasm_file,         NULL)
2196    PHP_FE(xcache_dasm_string,       NULL)
2197#endif
2198#ifdef HAVE_XCACHE_ENCODER
2199    PHP_FE(xcache_encode,            NULL)
2200#endif
2201#ifdef HAVE_XCACHE_DECODER
[9]2202    PHP_FE(xcache_decode_file,       NULL)
2203    PHP_FE(xcache_decode_string,     NULL)
[1]2204#endif
[27]2205#ifdef HAVE_XCACHE_COVERAGER
2206    PHP_FE(xcache_coverager_decode,  NULL)
[204]2207    PHP_FE(xcache_coverager_start,   NULL)
2208    PHP_FE(xcache_coverager_stop,    NULL)
2209    PHP_FE(xcache_coverager_get,     NULL)
[1]2210#endif
2211    PHP_FE(xcache_get_special_value, NULL)
2212    PHP_FE(xcache_get_op_type,       NULL)
2213    PHP_FE(xcache_get_data_type,     NULL)
2214    PHP_FE(xcache_get_opcode,        NULL)
[8]2215#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
[1]2216    PHP_FE(xcache_get_opcode_spec,   NULL)
[8]2217#endif
[1]2218    PHP_FE(xcache_is_autoglobal,     NULL)
2219    PHP_FE(xcache_inc,               NULL)
2220    PHP_FE(xcache_dec,               NULL)
2221    PHP_FE(xcache_get,               NULL)
2222    PHP_FE(xcache_set,               NULL)
2223    PHP_FE(xcache_isset,             NULL)
2224    PHP_FE(xcache_unset,             NULL)
2225    {NULL, NULL,                     NULL}
2226};
2227/* }}} */
2228
[75]2229/* old signal handlers {{{ */
2230typedef void (*xc_sighandler_t)(int);
2231#define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
2232#include "foreachcoresig.h"
2233#undef FOREACH_SIG
2234/* }}} */
2235static void xcache_signal_handler(int sig);
2236static void xcache_restore_signal_handler() /* {{{ */
[1]2237{
[75]2238#define FOREACH_SIG(sig) do { \
2239    if (old_##sig##_handler != xcache_signal_handler) { \
2240        signal(sig, old_##sig##_handler); \
2241    } \
2242    else { \
2243        signal(sig, SIG_DFL); \
2244    } \
2245} while (0)
2246#include "foreachcoresig.h"
2247#undef FOREACH_SIG
2248}
2249/* }}} */
2250static void xcache_init_signal_handler() /* {{{ */
2251{
2252#define FOREACH_SIG(sig) \
2253    old_##sig##_handler = signal(sig, xcache_signal_handler)
2254#include "foreachcoresig.h"
2255#undef FOREACH_SIG
2256}
2257/* }}} */
2258static void xcache_signal_handler(int sig) /* {{{ */
2259{
2260    xcache_restore_signal_handler();
[1]2261    if (xc_coredump_dir && xc_coredump_dir[0]) {
2262        chdir(xc_coredump_dir);
2263    }
[65]2264    raise(sig);
[1]2265}
2266/* }}} */
2267
2268/* {{{ PHP_INI */
2269
[164]2270static PHP_INI_MH(xc_OnUpdateDummy)
2271{
2272    return SUCCESS;
2273}
2274
2275static PHP_INI_MH(xc_OnUpdateULong)
2276{
2277    zend_ulong *p = (zend_ulong *) mh_arg1;
2278
2279    *p = (zend_ulong) atoi(new_value);
2280    return SUCCESS;
2281}
2282
[1]2283static PHP_INI_MH(xc_OnUpdateBool)
2284{
2285    zend_bool *p = (zend_bool *)mh_arg1;
2286
2287    if (strncasecmp("on", new_value, sizeof("on"))) {
2288        *p = (zend_bool) atoi(new_value);
2289    }
2290    else {
2291        *p = (zend_bool) 1;
2292    }
2293    return SUCCESS;
2294}
2295
2296static PHP_INI_MH(xc_OnUpdateString)
2297{
2298    char **p = (char**)mh_arg1;
2299    if (*p) {
2300        pefree(*p, 1);
2301    }
2302    *p = pemalloc(strlen(new_value) + 1, 1);
2303    strcpy(*p, new_value);
2304    return SUCCESS;
2305}
[82]2306
[114]2307#ifndef ZEND_ENGINE_2
2308#define OnUpdateLong OnUpdateInt
[1]2309#endif
2310
[21]2311#ifdef ZEND_WIN32
2312#   define DEFAULT_PATH "xcache"
2313#else
2314#   define DEFAULT_PATH "/dev/zero"
2315#endif
[1]2316PHP_INI_BEGIN()
[21]2317    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_mmap_path)
[1]2318    PHP_INI_ENTRY1     ("xcache.coredump_directory",      "", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coredump_dir)
2319    PHP_INI_ENTRY1     ("xcache.test",                   "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_test)
2320    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_readonly_protection)
[164]2321    /* opcode cache */
2322    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2323    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2324    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2325    PHP_INI_ENTRY1     ("xcache.shm_scheme",          "mmap", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_shm_scheme)
2326    PHP_INI_ENTRY1     ("xcache.ttl",                    "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_ttl)
2327    PHP_INI_ENTRY1     ("xcache.gc_interval",            "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_gc_interval)
2328    /* var cache */
2329    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2330    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2331    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2332    PHP_INI_ENTRY1     ("xcache.var_maxttl",             "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_maxttl)
2333    PHP_INI_ENTRY1     ("xcache.var_gc_interval",      "120", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_gc_interval)
[1]2334
2335    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,        cacher,            zend_xcache_globals, xcache_globals)
[165]2336    STD_PHP_INI_BOOLEAN("xcache.stat",                   "1", PHP_INI_ALL,    OnUpdateBool,        stat,              zend_xcache_globals, xcache_globals)
[1]2337#ifdef HAVE_XCACHE_OPTIMIZER
2338    STD_PHP_INI_BOOLEAN("xcache.optimizer",              "0", PHP_INI_ALL,    OnUpdateBool,        optimizer,         zend_xcache_globals, xcache_globals)
2339#endif
[254]2340    STD_PHP_INI_ENTRY  ("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong,        var_ttl,           zend_xcache_globals, xcache_globals)
[27]2341#ifdef HAVE_XCACHE_COVERAGER
[204]2342    STD_PHP_INI_BOOLEAN("xcache.coverager"      ,        "0", PHP_INI_ALL,    OnUpdateBool,        coverager,         zend_xcache_globals, xcache_globals)
2343    PHP_INI_ENTRY1     ("xcache.coveragedump_directory",  "", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
[1]2344#endif
2345PHP_INI_END()
2346/* }}} */
2347/* {{{ PHP_MINFO_FUNCTION(xcache) */
2348static PHP_MINFO_FUNCTION(xcache)
2349{
[82]2350    char buf[100];
2351    char *ptr;
[163]2352    int left, len;
2353    xc_shm_scheme_t *scheme;
[204]2354    char *covdumpdir;
[82]2355
[1]2356    php_info_print_table_start();
[148]2357    php_info_print_table_header(2, "XCache Support", "enabled");
[1]2358    php_info_print_table_row(2, "Version", XCACHE_VERSION);
[26]2359    php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
[1]2360    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A");
[82]2361
2362    if (xc_php_size) {
2363        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
[163]2364        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
[82]2365        php_info_print_table_row(2, "Opcode Cache", buf);
2366        efree(ptr);
2367    }
2368    else {
2369        php_info_print_table_row(2, "Opcode Cache", "disabled");
2370    }
2371    if (xc_var_size) {
2372        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
[163]2373        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
[82]2374        php_info_print_table_row(2, "Variable Cache", buf);
2375        efree(ptr);
2376    }
2377    else {
2378        php_info_print_table_row(2, "Variable Cache", "disabled");
2379    }
[163]2380
2381    left = sizeof(buf);
2382    ptr = buf;
2383    buf[0] = '\0';
2384    for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
2385        len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
2386        left -= len;
2387        ptr += len;
2388    }
2389    php_info_print_table_row(2, "Shared Memory Schemes", buf);
2390
[26]2391#ifdef HAVE_XCACHE_COVERAGER
[204]2392    if (cfg_get_string("xcache.coveragedump_directory", &covdumpdir) != SUCCESS || !covdumpdir[0]) {
2393        covdumpdir = NULL;
2394    }
2395    php_info_print_table_row(2, "Coverage Auto Dumper", XG(coverager) && covdumpdir ? "enabled" : "disabled");
[26]2396#endif
[1]2397    php_info_print_table_end();
[82]2398
[1]2399    DISPLAY_INI_ENTRIES();
2400}
2401/* }}} */
2402/* {{{ extension startup */
2403static void xc_zend_extension_register(zend_extension *new_extension, DL_HANDLE handle)
2404{
2405    zend_extension extension;
2406
2407    extension = *new_extension;
2408    extension.handle = handle;
2409
2410    zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
2411
[296]2412    zend_llist_prepend_element(&zend_extensions, &extension);
[1]2413#ifdef DEBUG
2414    fprintf(stderr, "registered\n");
2415#endif
2416}
2417
2418static int xc_zend_extension_startup(zend_extension *extension)
2419{
2420    if (extension->startup) {
2421        if (extension->startup(extension) != SUCCESS) {
2422            return FAILURE;
2423        }
2424    }
2425    return SUCCESS;
2426}
2427/* }}} */
[278]2428static int xc_ptr_compare_func(void *p1, void *p2) /* {{{ */
2429{
2430    return p1 == p2;
2431}
2432/* }}} */
2433static int xc_zend_remove_extension(zend_extension *extension) /* {{{ */
2434{
2435    llist_dtor_func_t dtor;
2436
2437    assert(extension);
2438    dtor = zend_extensions.dtor; /* avoid dtor */
2439    zend_extensions.dtor = NULL;
2440    zend_llist_del_element(&zend_extensions, extension, xc_ptr_compare_func);
2441    zend_extensions.dtor = dtor;
[295]2442    return SUCCESS;
[278]2443}
2444/* }}} */
[82]2445static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
2446{
2447    int bits, size;
2448    char *value;
2449
2450    if (cfg_get_string(name, &value) != SUCCESS) {
2451        value = default_value;
2452    }
2453
2454    p->size = zend_atoi(value, strlen(value));
2455    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
2456        /* empty body */
2457    }
2458    p->size = size;
2459    p->bits = bits;
2460    p->mask = size - 1;
2461
2462    return SUCCESS;
2463}
2464/* }}} */
[90]2465static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
[82]2466{
2467    char *value;
2468
2469    if (cfg_get_string(name, &value) != SUCCESS) {
2470        value = default_value;
2471    }
2472
2473    *p = zend_atoi(value, strlen(value));
2474    return SUCCESS;
2475}
2476/* }}} */
[1]2477/* {{{ PHP_MINIT_FUNCTION(xcache) */
2478static PHP_MINIT_FUNCTION(xcache)
2479{
2480    char *env;
[189]2481    zend_extension *ext;
2482    zend_llist_position lpos;
[1]2483
2484    xc_module_gotup = 1;
2485    if (!xc_zend_extension_gotup) {
[296]2486        xc_zend_extension_register(&zend_extension_entry, 0);
2487        xc_zend_extension_startup(&zend_extension_entry);
2488        xc_zend_extension_faked = 1;
[1]2489    }
2490
[190]2491    ext = zend_get_extension("Zend Optimizer");
2492    if (ext) {
2493        /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
2494        ext->op_array_handler = NULL;
2495    }
[189]2496    /* cache if there's an op_array_ctor */
2497    for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
2498            ext;
2499            ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
2500        if (ext->op_array_ctor) {
2501            xc_have_op_array_ctor = 1;
2502            break;
2503        }
2504    }
2505
2506
[92]2507#ifndef PHP_GINIT
[17]2508    ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
[92]2509#endif
[17]2510    REGISTER_INI_ENTRIES();
2511
[1]2512    if (strcmp(sapi_module.name, "cli") == 0) {
2513        if ((env = getenv("XCACHE_TEST")) != NULL) {
2514            zend_alter_ini_entry("xcache.test", sizeof("xcache.test"), env, strlen(env) + 1, PHP_INI_SYSTEM, PHP_INI_STAGE_STARTUP);
2515        }
2516        if (!xc_test) {
2517            /* disable cache for cli except for test */
2518            xc_php_size = xc_var_size = 0;
2519        }
2520    }
2521
[114]2522    xc_config_long(&xc_php_size,       "xcache.size",        "0");
2523    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
2524    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
[82]2525
[164]2526    xc_config_long(&xc_var_size,       "xcache.var_size",    "0");
2527    xc_config_hash(&xc_var_hcache,     "xcache.var_count",   "1");
2528    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",  "8K");
[82]2529
[1]2530    if (xc_php_size <= 0) {
2531        xc_php_size = xc_php_hcache.size = 0;
2532    }
2533    if (xc_var_size <= 0) {
2534        xc_var_size = xc_var_hcache.size = 0;
2535    }
2536
[65]2537    if (xc_coredump_dir && xc_coredump_dir[0]) {
[75]2538        xcache_init_signal_handler();
[65]2539    }
[1]2540
2541    xc_init_constant(module_number TSRMLS_CC);
[148]2542    xc_shm_init_modules();
[1]2543
2544    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
[334]2545        if (xc_init(module_number TSRMLS_CC) != SUCCESS) {
[21]2546            zend_error(E_ERROR, "XCache: Cannot init");
[1]2547            goto err_init;
2548        }
2549        xc_initized = 1;
2550    }
2551
[27]2552#ifdef HAVE_XCACHE_COVERAGER
2553    xc_coverager_init(module_number TSRMLS_CC);
[1]2554#endif
2555
2556    return SUCCESS;
2557
2558err_init:
2559    return FAILURE;
2560}
2561/* }}} */
2562/* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
2563static PHP_MSHUTDOWN_FUNCTION(xcache)
2564{
2565    if (xc_initized) {
2566        xc_destroy();
2567    }
2568    if (xc_mmap_path) {
2569        pefree(xc_mmap_path, 1);
2570        xc_mmap_path = NULL;
2571    }
[148]2572    if (xc_shm_scheme) {
2573        pefree(xc_shm_scheme, 1);
2574        xc_shm_scheme = NULL;
2575    }
[1]2576
[27]2577#ifdef HAVE_XCACHE_COVERAGER
2578    xc_coverager_destroy();
[1]2579#endif
2580
[65]2581    if (xc_coredump_dir && xc_coredump_dir[0]) {
[75]2582        xcache_restore_signal_handler();
[65]2583    }
[1]2584    if (xc_coredump_dir) {
2585        pefree(xc_coredump_dir, 1);
2586        xc_coredump_dir = NULL;
2587    }
[92]2588#ifndef PHP_GINIT
2589#   ifdef ZTS
[25]2590    ts_free_id(xcache_globals_id);
[92]2591#   else
[1]2592    xc_shutdown_globals(&xcache_globals TSRMLS_CC);
[92]2593#   endif
[1]2594#endif
2595
[278]2596    if (xc_zend_extension_faked) {
2597        zend_extension *ext = zend_get_extension(XCACHE_NAME);
2598        if (ext->shutdown) {
2599            ext->shutdown(ext);
2600        }
2601        xc_zend_remove_extension(ext);
2602    }
[1]2603    UNREGISTER_INI_ENTRIES();
2604    return SUCCESS;
2605}
2606/* }}} */
2607/* {{{ PHP_RINIT_FUNCTION(xcache) */
2608static PHP_RINIT_FUNCTION(xcache)
2609{
2610    xc_request_init(TSRMLS_C);
2611    return SUCCESS;
2612}
2613/* }}} */
2614/* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
2615#ifndef ZEND_ENGINE_2
2616static PHP_RSHUTDOWN_FUNCTION(xcache)
2617#else
2618static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
2619#endif
2620{
[18]2621#ifdef ZEND_ENGINE_2
[11]2622    TSRMLS_FETCH();
[18]2623#endif
[11]2624
[1]2625    xc_request_shutdown(TSRMLS_C);
2626    return SUCCESS;
2627}
2628/* }}} */
2629/* {{{ module definition structure */
2630
2631zend_module_entry xcache_module_entry = {
2632    STANDARD_MODULE_HEADER,
[340]2633    XCACHE_NAME,
[1]2634    xcache_functions,
2635    PHP_MINIT(xcache),
2636    PHP_MSHUTDOWN(xcache),
2637    PHP_RINIT(xcache),
2638#ifndef ZEND_ENGINE_2
2639    PHP_RSHUTDOWN(xcache),
2640#else
2641    NULL,
2642#endif
2643    PHP_MINFO(xcache),
2644    XCACHE_VERSION,
[92]2645#ifdef PHP_GINIT
2646    PHP_MODULE_GLOBALS(xcache),
2647    PHP_GINIT(xcache),
2648    PHP_GSHUTDOWN(xcache),
2649#endif
[1]2650#ifdef ZEND_ENGINE_2
2651    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
2652#else
2653    NULL,
2654    NULL,
2655#endif
2656    STANDARD_MODULE_PROPERTIES_EX
2657};
2658
2659#ifdef COMPILE_DL_XCACHE
2660ZEND_GET_MODULE(xcache)
2661#endif
2662/* }}} */
[190]2663static startup_func_t xc_last_ext_startup;
2664static zend_llist_element *xc_llist_element;
2665static int xc_zend_startup_last(zend_extension *extension) /* {{{ */
2666{
2667    /* restore */
2668    extension->startup = xc_last_ext_startup;
2669    if (extension->startup) {
2670        if (extension->startup(extension) != SUCCESS) {
2671            return FAILURE;
2672        }
2673    }
2674    xc_zend_extension_register(&zend_extension_entry, 0);
2675    if (!xc_module_gotup) {
2676        return zend_startup_module(&xcache_module_entry);
2677    }
2678    return SUCCESS;
2679}
2680/* }}} */
[1]2681ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
2682{
2683    xc_zend_extension_gotup = 1;
[190]2684    xc_llist_element = NULL;
2685    if (zend_llist_count(&zend_extensions) > 1) {
2686        zend_llist_position lpos;
2687        zend_extension *ext;
2688
2689        ext = zend_get_extension(XCACHE_NAME);
[278]2690        xc_zend_remove_extension(ext);
[190]2691
2692        ext = (zend_extension *) zend_llist_get_last_ex(&zend_extensions, &lpos);
2693        assert(ext);
2694        xc_last_ext_startup = ext->startup;
2695        ext->startup = xc_zend_startup_last;
2696    }
2697    else if (!xc_module_gotup) {
[1]2698        return zend_startup_module(&xcache_module_entry);
2699    }
2700    return SUCCESS;
2701}
2702/* }}} */
2703ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
2704{
2705    /* empty */
2706}
2707/* }}} */
2708ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
2709{
[27]2710#ifdef HAVE_XCACHE_COVERAGER
2711    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
[1]2712#endif
2713}
2714/* }}} */
2715ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
2716{
2717#if 0
[27]2718    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
[1]2719#endif
2720}
2721/* }}} */
2722ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
2723{
2724#if 0
[27]2725    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
[1]2726#endif
2727}
2728/* }}} */
2729/* {{{ zend extension definition structure */
2730ZEND_DLEXPORT zend_extension zend_extension_entry = {
2731    XCACHE_NAME,
2732    XCACHE_VERSION,
2733    XCACHE_AUTHOR,
2734    XCACHE_URL,
2735    XCACHE_COPYRIGHT,
2736    xcache_zend_startup,
2737    xcache_zend_shutdown,
2738    NULL,           /* activate_func_t */
2739    NULL,           /* deactivate_func_t */
2740    NULL,           /* message_handler_func_t */
2741    NULL,           /* op_array_handler_func_t */
2742    xcache_statement_handler,
2743    xcache_fcall_begin_handler,
2744    xcache_fcall_end_handler,
2745    NULL,           /* op_array_ctor_func_t */
2746    NULL,           /* op_array_dtor_func_t */
2747    STANDARD_ZEND_EXTENSION_PROPERTIES
2748};
2749
2750#ifndef ZEND_EXT_API
2751#   define ZEND_EXT_API ZEND_DLEXPORT
2752#endif
2753#if COMPILE_DL_XCACHE
2754ZEND_EXTENSION();
2755#endif
2756/* }}} */
Note: See TracBrowser for help on using the repository browser.