source: trunk/xcache.c @ 148

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

reconstruct shm/allocator

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