source: svn/trunk/xcache.c @ 137

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

changed the coding way of unlinking list. fixed ttl expires dead loop

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