source: branches/1.1/xcache.c @ 256

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

trunk->1.1

  • merged [254] fix xcache.var_ttl displaying in info, fix xcache_get for ttl-unlimited
  • merged [250] admin: fix switcher class on active
  • merged [249] return 0 s instead of empty for deltatime
  • merged [201] coverager: avoid crash when upstream failed to compile file
File size: 62.2 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 TSRMLS_DC) /* {{{ */
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 TSRMLS_DC) /* {{{ */
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 TSRMLS_CC);
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 TSRMLS_CC);
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    CG(in_compilation)    = 1;
988    CG(compiled_filename) = stored_xce->name.str.val;
989    CG(zend_lineno)       = 0;
990#ifdef DEBUG
991    fprintf(stderr, "restoring\n");
992#endif
993    xc_processor_restore_xc_entry_t(&xce, stored_xce, xc_readonly_protection TSRMLS_CC);
994
995    catched = 0;
996    zend_try {
997        op_array = xc_entry_install(&xce, h TSRMLS_CC);
998    } zend_catch {
999        catched = 1;
1000    } zend_end_try();
1001
1002#define X_FREE(var) \
1003    if (xce.data.php->var) { \
1004        efree(xce.data.php->var); \
1005    }
1006    X_FREE(classinfos)
1007    X_FREE(funcinfos)
1008#ifdef HAVE_XCACHE_CONSTANT
1009    X_FREE(constinfos)
1010#endif
1011#undef X_FREE
1012    efree(xce.data.php);
1013
1014    if (catched) {
1015        zend_bailout();
1016    }
1017    CG(in_compilation)    = 0;
1018    CG(compiled_filename) = NULL;
1019#ifdef DEBUG
1020    fprintf(stderr, "restored\n");
1021#endif
1022    return op_array;
1023}
1024/* }}} */
1025
1026/* gdb helper functions, but N/A for coredump */
1027int xc_is_rw(const void *p) /* {{{ */
1028{
1029    xc_shm_t *shm;
1030    int i;
1031    if (!xc_initized) {
1032        return 0;
1033    }
1034    for (i = 0; i < xc_php_hcache.size; i ++) {
1035        shm = xc_php_caches[i]->shm;
1036        if (shm->handlers->is_readwrite(shm, p)) {
1037            return 1;
1038        }
1039    }
1040    for (i = 0; i < xc_var_hcache.size; i ++) {
1041        shm = xc_var_caches[i]->shm;
1042        if (shm->handlers->is_readwrite(shm, p)) {
1043            return 1;
1044        }
1045    }
1046    return 0;
1047}
1048/* }}} */
1049int xc_is_ro(const void *p) /* {{{ */
1050{
1051    xc_shm_t *shm;
1052    int i;
1053    if (!xc_initized) {
1054        return 0;
1055    }
1056    for (i = 0; i < xc_php_hcache.size; i ++) {
1057        shm = xc_php_caches[i]->shm;
1058        if (shm->handlers->is_readonly(shm, p)) {
1059            return 1;
1060        }
1061    }
1062    for (i = 0; i < xc_var_hcache.size; i ++) {
1063        shm = xc_var_caches[i]->shm;
1064        if (shm->handlers->is_readonly(shm, p)) {
1065            return 1;
1066        }
1067    }
1068    return 0;
1069}
1070/* }}} */
1071int xc_is_shm(const void *p) /* {{{ */
1072{
1073    return xc_is_ro(p) || xc_is_rw(p);
1074}
1075/* }}} */
1076
1077/* module helper function */
1078static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
1079{
1080    typedef struct {
1081        const char *prefix;
1082        zend_uchar (*getsize)();
1083        const char *(*get)(zend_uchar i);
1084    } xc_meminfo_t;
1085    xc_meminfo_t nameinfos[] = {
1086        { "",        xc_get_op_type_count,   xc_get_op_type   },
1087        { "",        xc_get_data_type_count, xc_get_data_type },
1088        { "",        xc_get_opcode_count,    xc_get_opcode    },
1089        { "OPSPEC_", xc_get_op_spec_count,   xc_get_op_spec   },
1090        { NULL, NULL, NULL }
1091    };
1092    xc_meminfo_t* p;
1093    zend_uchar i, count;
1094    char const_name[96];
1095    int const_name_len;
1096    int undefdone = 0;
1097
1098    for (p = nameinfos; p->getsize; p ++) {
1099        count = p->getsize();
1100        for (i = 0; i < count; i ++) {
1101            const char *name = p->get(i);
1102            if (!name) continue;
1103            if (strcmp(name, "UNDEF") == 0) {
1104                if (undefdone) continue;
1105                undefdone = 1;
1106            }
1107            const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
1108            zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1109        }
1110    }
1111
1112    zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1113    zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1114    zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1115    zend_register_stringl_constant(ZEND_STRS("XCACHE_VERSION"), ZEND_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1116    zend_register_stringl_constant(ZEND_STRS("XCACHE_MODULES"), ZEND_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1117    return 0;
1118}
1119/* }}} */
1120static xc_shm_t *xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache) /* {{{ */
1121{
1122    int i;
1123    xc_cache_t *cache;
1124    xc_shm_t *shm;
1125
1126    if (!caches) {
1127        return NULL;
1128    }
1129    shm = NULL;
1130    for (i = 0; i < hcache->size; i ++) {
1131        cache = caches[i];
1132        if (cache) {
1133            if (cache->lck) {
1134                xc_lock_destroy(cache->lck);
1135            }
1136            /* do NOT free
1137            if (cache->entries) {
1138                cache->mem->handlers->free(cache->mem, cache->entries);
1139            }
1140            cache->mem->handlers->free(cache->mem, cache);
1141            */
1142            shm = cache->shm;
1143            shm->handlers->memdestroy(cache->mem);
1144        }
1145    }
1146    free(caches);
1147    return shm;
1148}
1149/* }}} */
1150static xc_cache_t **xc_cache_init(xc_shm_t *shm, xc_hash_t *hcache, xc_hash_t *hentry, xc_shmsize_t shmsize) /* {{{ */
1151{
1152    xc_cache_t **caches = NULL, *cache;
1153    xc_mem_t *mem;
1154    time_t now = time(NULL);
1155    int i;
1156    xc_memsize_t memsize;
1157
1158    memsize = shmsize / hcache->size;
1159
1160    /* Don't let it break out of mem after ALIGNed
1161     * This is important for
1162     * Simply loop until it fit our need
1163     */
1164    while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
1165        if (memsize < ALIGN(1)) {
1166            CHECK(NULL, "cache too small");
1167        }
1168        memsize --;
1169    }
1170
1171    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
1172
1173    for (i = 0; i < hcache->size; i ++) {
1174        CHECK(mem            = shm->handlers->meminit(shm, memsize), "Failed init memory allocator");
1175        CHECK(cache          = mem->handlers->calloc(mem, 1, sizeof(xc_cache_t)), "cache OOM");
1176        CHECK(cache->entries = mem->handlers->calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
1177        CHECK(cache->lck     = xc_lock_init(NULL), "can't create lock");
1178
1179        cache->hcache  = hcache;
1180        cache->hentry  = hentry;
1181        cache->shm     = shm;
1182        cache->mem     = mem;
1183        cache->cacheid = i;
1184        cache->last_gc_deletes = now;
1185        cache->last_gc_expires = now;
1186        caches[i] = cache;
1187    }
1188    return caches;
1189
1190err:
1191    if (caches) {
1192        xc_cache_destroy(caches, hcache);
1193    }
1194    return NULL;
1195}
1196/* }}} */
1197static void xc_destroy() /* {{{ */
1198{
1199    xc_shm_t *shm = NULL;
1200
1201    if (origin_compile_file) {
1202        zend_compile_file = origin_compile_file;
1203        origin_compile_file = NULL;
1204    }
1205
1206    if (xc_php_caches) {
1207        shm = xc_cache_destroy(xc_php_caches, &xc_php_hcache);
1208        xc_php_caches = NULL;
1209    }
1210    if (xc_var_caches) {
1211        shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
1212        xc_var_caches = NULL;
1213    }
1214    if (shm) {
1215        xc_shm_destroy(shm);
1216    }
1217}
1218/* }}} */
1219static int xc_init(int module_number TSRMLS_DC) /* {{{ */
1220{
1221    xc_shm_t *shm;
1222
1223    xc_php_caches = xc_var_caches = NULL;
1224
1225    if (xc_php_size || xc_var_size) {
1226        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");
1227        if (!shm->handlers->can_readonly(shm)) {
1228            xc_readonly_protection = 0;
1229        }
1230
1231        if (xc_php_size) {
1232            origin_compile_file = zend_compile_file;
1233            zend_compile_file = xc_compile_file;
1234
1235            CHECK(xc_php_caches = xc_cache_init(shm, &xc_php_hcache, &xc_php_hentry, xc_php_size), "failed init opcode cache");
1236        }
1237
1238        if (xc_var_size) {
1239            CHECK(xc_var_caches = xc_cache_init(shm, &xc_var_hcache, &xc_var_hentry, xc_var_size), "failed init variable cache");
1240        }
1241    }
1242    return 1;
1243
1244err:
1245    if (xc_php_caches || xc_var_caches) {
1246        xc_destroy();
1247        /* shm destroied */
1248    }
1249    else if (shm) {
1250        xc_shm_destroy(shm);
1251    }
1252    return 0;
1253}
1254/* }}} */
1255static void xc_request_init(TSRMLS_D) /* {{{ */
1256{
1257    int i;
1258
1259    if (xc_php_hcache.size && !XG(php_holds)) {
1260        XG(php_holds) = calloc(xc_php_hcache.size, sizeof(xc_stack_t));
1261        for (i = 0; i < xc_php_hcache.size; i ++) {
1262            xc_stack_init(&XG(php_holds[i]));
1263        }
1264    }
1265
1266    if (xc_var_hcache.size && !XG(var_holds)) {
1267        XG(var_holds) = calloc(xc_var_hcache.size, sizeof(xc_stack_t));
1268        for (i = 0; i < xc_var_hcache.size; i ++) {
1269            xc_stack_init(&XG(var_holds[i]));
1270        }
1271    }
1272
1273    if (XG(cacher)) {
1274#if PHP_API_VERSION <= 20041225
1275        XG(request_time) = time(NULL);
1276#else
1277        XG(request_time) = sapi_get_request_time(TSRMLS_C);
1278#endif
1279    }
1280#ifdef HAVE_XCACHE_COVERAGER
1281    xc_coverager_request_init(TSRMLS_C);
1282#endif
1283}
1284/* }}} */
1285static void xc_request_shutdown(TSRMLS_D) /* {{{ */
1286{
1287    xc_entry_unholds(TSRMLS_C);
1288    xc_gc_expires_php(TSRMLS_C);
1289    xc_gc_expires_var(TSRMLS_C);
1290    xc_gc_deletes(TSRMLS_C);
1291#ifdef HAVE_XCACHE_COVERAGER
1292    xc_coverager_request_shutdown(TSRMLS_C);
1293#endif
1294}
1295/* }}} */
1296/* {{{ PHP_GINIT_FUNCTION(xcache) */
1297static
1298#ifdef PHP_GINIT_FUNCTION
1299PHP_GINIT_FUNCTION(xcache)
1300#else
1301void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
1302#endif
1303{
1304    memset(xcache_globals, 0, sizeof(zend_xcache_globals));
1305}
1306/* }}} */
1307/* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
1308static
1309#ifdef PHP_GSHUTDOWN_FUNCTION
1310PHP_GSHUTDOWN_FUNCTION(xcache)
1311#else
1312void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
1313#endif
1314{
1315    int i;
1316
1317    if (xcache_globals->php_holds != NULL) {
1318        for (i = 0; i < xc_php_hcache.size; i ++) {
1319            xc_stack_destroy(&xcache_globals->php_holds[i]);
1320        }
1321        free(xcache_globals->php_holds);
1322        xcache_globals->php_holds = NULL;
1323    }
1324
1325    if (xcache_globals->var_holds != NULL) {
1326        for (i = 0; i < xc_var_hcache.size; i ++) {
1327            xc_stack_destroy(&xcache_globals->var_holds[i]);
1328        }
1329        free(xcache_globals->var_holds);
1330        xcache_globals->var_holds = NULL;
1331    }
1332}
1333/* }}} */
1334
1335/* user functions */
1336static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
1337{
1338    zval **server = NULL;
1339    zval **user = NULL;
1340    zval **pass = NULL;
1341    char *admin_user = NULL;
1342    char *admin_pass = NULL;
1343    HashTable *ht;
1344
1345    if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
1346        admin_user = NULL;
1347    }
1348    if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
1349        admin_pass = NULL;
1350    }
1351
1352    if (admin_user == NULL || admin_pass == NULL) {
1353        php_error_docref(NULL TSRMLS_CC, E_ERROR, "xcache.admin.user and xcache.admin.pass is required");
1354        zend_bailout();
1355    }
1356    if (strlen(admin_pass) != 32) {
1357        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));
1358        zend_bailout();
1359    }
1360
1361#ifdef ZEND_ENGINE_2_1
1362    zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
1363#endif
1364    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
1365        php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
1366        zend_bailout();
1367    }
1368    ht = HASH_OF((*server));
1369
1370    if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
1371        user = NULL;
1372    }
1373    else if (Z_TYPE_PP(user) != IS_STRING) {
1374        user = NULL;
1375    }
1376
1377    if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
1378        pass = NULL;
1379    }
1380    else if (Z_TYPE_PP(pass) != IS_STRING) {
1381        pass = NULL;
1382    }
1383
1384    if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
1385        PHP_MD5_CTX context;
1386        char md5str[33];
1387        unsigned char digest[16];
1388
1389        PHP_MD5Init(&context);
1390        PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
1391        PHP_MD5Final(digest, &context);
1392
1393        md5str[0] = '\0';
1394        make_digest(md5str, digest);
1395        if (strcmp(admin_pass, md5str) == 0) {
1396            return 1;
1397        }
1398    }
1399
1400#define STR "WWW-authenticate: basic realm='XCache Administration'"
1401    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1402#undef STR
1403#define STR "HTTP/1.0 401 Unauthorized"
1404    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1405#undef STR
1406    ZEND_PUTS("XCache Auth Failed. User and Password is case sense\n");
1407
1408    zend_bailout();
1409    return 0;
1410}
1411/* }}} */
1412/* {{{ xcache_admin_operate */
1413typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
1414static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
1415{
1416    long type;
1417    int size;
1418    xc_cache_t **caches, *cache;
1419    long id = 0;
1420
1421    if (!xc_initized) {
1422        php_error_docref(NULL TSRMLS_CC, E_WARNING, "XCache is not initized");
1423        RETURN_FALSE;
1424    }
1425
1426    xcache_admin_auth_check(TSRMLS_C);
1427
1428    if (optype == XC_OP_COUNT) {
1429        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
1430            return;
1431        }
1432    }
1433    else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
1434        return;
1435    }
1436
1437    switch (type) {
1438        case XC_TYPE_PHP:
1439            size = xc_php_hcache.size;
1440            caches = xc_php_caches;
1441            break;
1442
1443        case XC_TYPE_VAR:
1444            size = xc_var_hcache.size;
1445            caches = xc_var_caches;
1446            break;
1447
1448        default:
1449            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
1450            RETURN_FALSE;
1451    }
1452
1453    switch (optype) {
1454        case XC_OP_COUNT:
1455            RETURN_LONG(size)
1456            break;
1457
1458        case XC_OP_INFO:
1459        case XC_OP_LIST:
1460            if (id < 0 || id >= size) {
1461                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1462                RETURN_FALSE;
1463            }
1464
1465            array_init(return_value);
1466
1467            cache = caches[id];
1468            ENTER_LOCK(cache) {
1469                if (optype == XC_OP_INFO) {
1470                    xc_fillinfo_dmz(type, cache, return_value TSRMLS_CC);
1471                }
1472                else {
1473                    xc_filllist_dmz(cache, return_value TSRMLS_CC);
1474                }
1475            } LEAVE_LOCK(cache);
1476            break;
1477        case XC_OP_CLEAR:
1478            {
1479                xc_entry_t *e, *next;
1480                int i, c;
1481
1482                if (id < 0 || id >= size) {
1483                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1484                    RETURN_FALSE;
1485                }
1486
1487                cache = caches[id];
1488                ENTER_LOCK(cache) {
1489                    for (i = 0, c = cache->hentry->size; i < c; i ++) {
1490                        for (e = cache->entries[i]; e; e = next) {
1491                            next = e->next;
1492                            xc_entry_remove_dmz(e TSRMLS_CC);
1493                        }
1494                        cache->entries[i] = NULL;
1495                    }
1496                } LEAVE_LOCK(cache);
1497                xc_gc_deletes(TSRMLS_C);
1498            }
1499            break;
1500
1501        default:
1502            assert(0);
1503    }
1504}
1505/* }}} */
1506/* {{{ proto int xcache_count(int type)
1507   Return count of cache on specified cache type */
1508PHP_FUNCTION(xcache_count)
1509{
1510    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1511}
1512/* }}} */
1513/* {{{ proto array xcache_info(int type, int id)
1514   Get cache info by id on specified cache type */
1515PHP_FUNCTION(xcache_info)
1516{
1517    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1518}
1519/* }}} */
1520/* {{{ proto array xcache_list(int type, int id)
1521   Get cache entries list by id on specified cache type */
1522PHP_FUNCTION(xcache_list)
1523{
1524    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1525}
1526/* }}} */
1527/* {{{ proto array xcache_clear_cache(int type, int id)
1528   Clear cache by id on specified cache type */
1529PHP_FUNCTION(xcache_clear_cache)
1530{
1531    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1532}
1533/* }}} */
1534
1535static int xc_entry_init_key_var(xc_entry_t *xce, zval *name TSRMLS_DC) /* {{{ */
1536{
1537    xc_hash_value_t hv;
1538    int cacheid;
1539
1540    switch (Z_TYPE_P(name)) {
1541#ifdef IS_UNICODE
1542        case IS_UNICODE:
1543#endif
1544        case IS_STRING:
1545            break;
1546        default:
1547#ifdef IS_UNICODE
1548            convert_to_text(name);
1549#else
1550            convert_to_string(name);
1551#endif
1552    }
1553#ifdef IS_UNICODE
1554    xce->name_type = name->type;
1555#endif
1556    xce->name = name->value;
1557
1558    hv = xc_entry_hash_var(xce TSRMLS_CC);
1559
1560    cacheid = (hv & xc_var_hcache.mask);
1561    xce->cache = xc_var_caches[cacheid];
1562    hv >>= xc_var_hcache.bits;
1563    xce->hvalue = (hv & xc_var_hentry.mask);
1564
1565    xce->type = XC_TYPE_VAR;
1566    return SUCCESS;
1567}
1568/* }}} */
1569/* {{{ proto mixed xcache_get(string name)
1570   Get cached data by specified name */
1571PHP_FUNCTION(xcache_get)
1572{
1573    xc_entry_t xce, *stored_xce;
1574    xc_entry_data_var_t var;
1575    zval *name;
1576
1577    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1578        return;
1579    }
1580    xce.data.var = &var;
1581    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1582
1583    ENTER_LOCK(xce.cache) {
1584        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1585        if (stored_xce) {
1586            if (!VAR_ENTRY_EXPIRED(stored_xce)) {
1587                xc_processor_restore_zval(return_value, stored_xce->data.var->value TSRMLS_CC);
1588                /* return */
1589                break;
1590            }
1591            else {
1592                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1593            }
1594        }
1595
1596        RETVAL_NULL();
1597    } LEAVE_LOCK(xce.cache);
1598}
1599/* }}} */
1600/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
1601   Store data to cache by specified name */
1602PHP_FUNCTION(xcache_set)
1603{
1604    xc_entry_t xce, *stored_xce;
1605    xc_entry_data_var_t var;
1606    zval *name;
1607    zval *value;
1608
1609    xce.ttl = XG(var_ttl);
1610    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &xce.ttl) == FAILURE) {
1611        return;
1612    }
1613
1614    /* max ttl */
1615    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
1616        xce.ttl = xc_var_maxttl;
1617    }
1618
1619    xce.data.var = &var;
1620    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1621
1622    ENTER_LOCK(xce.cache) {
1623        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1624        if (stored_xce) {
1625            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1626        }
1627        var.value = value;
1628        RETVAL_BOOL(xc_entry_store_dmz(&xce TSRMLS_CC) != NULL ? 1 : 0);
1629    } LEAVE_LOCK(xce.cache);
1630}
1631/* }}} */
1632/* {{{ proto bool  xcache_isset(string name)
1633   Check if an entry exists in cache by specified name */
1634PHP_FUNCTION(xcache_isset)
1635{
1636    xc_entry_t xce, *stored_xce;
1637    xc_entry_data_var_t var;
1638    zval *name;
1639
1640    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1641        return;
1642    }
1643    xce.data.var = &var;
1644    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1645
1646    ENTER_LOCK(xce.cache) {
1647        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1648        if (stored_xce) {
1649            if (!VAR_ENTRY_EXPIRED(stored_xce)) {
1650                RETVAL_TRUE;
1651                /* return */
1652                break;
1653            }
1654            else {
1655                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1656            }
1657        }
1658
1659        RETVAL_FALSE;
1660    } LEAVE_LOCK(xce.cache);
1661}
1662/* }}} */
1663/* {{{ proto bool  xcache_unset(string name)
1664   Unset existing data in cache by specified name */
1665PHP_FUNCTION(xcache_unset)
1666{
1667    xc_entry_t xce, *stored_xce;
1668    xc_entry_data_var_t var;
1669    zval *name;
1670
1671    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1672        return;
1673    }
1674    xce.data.var = &var;
1675    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1676
1677    ENTER_LOCK(xce.cache) {
1678        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1679        if (stored_xce) {
1680            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1681            RETVAL_TRUE;
1682        }
1683        else {
1684            RETVAL_FALSE;
1685        }
1686    } LEAVE_LOCK(xce.cache);
1687}
1688/* }}} */
1689static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
1690{
1691    xc_entry_t xce, *stored_xce;
1692    xc_entry_data_var_t var, *stored_var;
1693    zval *name;
1694    long count = 1;
1695    long value = 0;
1696    zval oldzval;
1697
1698    xce.ttl = XG(var_ttl);
1699    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &xce.ttl) == FAILURE) {
1700        return;
1701    }
1702
1703    /* max ttl */
1704    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
1705        xce.ttl = xc_var_maxttl;
1706    }
1707
1708    xce.data.var = &var;
1709    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1710
1711    ENTER_LOCK(xce.cache) {
1712        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1713        if (stored_xce) {
1714#ifdef DEBUG
1715            fprintf(stderr, "incdec: gotxce %s\n", xce.name.str.val);
1716#endif
1717            /* timeout */
1718            if (VAR_ENTRY_EXPIRED(stored_xce)) {
1719#ifdef DEBUG
1720                fprintf(stderr, "incdec: expired\n");
1721#endif
1722                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1723                stored_xce = NULL;
1724            }
1725            else {
1726                /* do it in place */
1727                stored_var = stored_xce->data.var;
1728                if (Z_TYPE_P(stored_var->value) == IS_LONG) {
1729                    stored_xce->ctime = XG(request_time);
1730                    stored_xce->ttl   = xce.ttl;
1731#ifdef DEBUG
1732                    fprintf(stderr, "incdec: islong\n");
1733#endif
1734                    value = Z_LVAL_P(stored_var->value);
1735                    value += (inc == 1 ? count : - count);
1736                    RETVAL_LONG(value);
1737                    Z_LVAL_P(stored_var->value) = value;
1738                    break; /* leave lock */
1739                }
1740                else {
1741#ifdef DEBUG
1742                    fprintf(stderr, "incdec: notlong\n");
1743#endif
1744                    xc_processor_restore_zval(&oldzval, stored_xce->data.var->value TSRMLS_CC);
1745                    convert_to_long(&oldzval);
1746                    value = Z_LVAL(oldzval);
1747                    zval_dtor(&oldzval);
1748                }
1749            }
1750        }
1751#ifdef DEBUG
1752        else {
1753            fprintf(stderr, "incdec: %s not found\n", xce.name.str.val);
1754        }
1755#endif
1756
1757        value += (inc == 1 ? count : - count);
1758        RETVAL_LONG(value);
1759        var.value = return_value;
1760
1761        if (stored_xce) {
1762            xce.atime = stored_xce->atime;
1763            xce.ctime = stored_xce->ctime;
1764            xce.hits  = stored_xce->hits;
1765            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
1766        }
1767        xc_entry_store_dmz(&xce TSRMLS_CC);
1768
1769    } LEAVE_LOCK(xce.cache);
1770}
1771/* }}} */
1772/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
1773   Increase an int counter in cache by specified name, create it if not exists */
1774PHP_FUNCTION(xcache_inc)
1775{
1776    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1777}
1778/* }}} */
1779/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
1780   Decrease an int counter in cache by specified name, create it if not exists */
1781PHP_FUNCTION(xcache_dec)
1782{
1783    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1784}
1785/* }}} */
1786/* {{{ proto string xcache_asm(string filename)
1787 */
1788#ifdef HAVE_XCACHE_ASSEMBLER
1789PHP_FUNCTION(xcache_asm)
1790{
1791}
1792#endif
1793/* }}} */
1794#ifdef HAVE_XCACHE_DISASSEMBLER
1795/* {{{ proto array  xcache_dasm_file(string filename)
1796   Disassemble file into opcode array by filename */
1797PHP_FUNCTION(xcache_dasm_file)
1798{
1799    char *filename;
1800    int filename_len;
1801
1802    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
1803        return;
1804    }
1805    if (!filename_len) RETURN_FALSE;
1806
1807    xc_dasm_file(return_value, filename TSRMLS_CC);
1808}
1809/* }}} */
1810/* {{{ proto array  xcache_dasm_string(string code)
1811   Disassemble php code into opcode array */
1812PHP_FUNCTION(xcache_dasm_string)
1813{
1814    zval *code;
1815
1816    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
1817        return;
1818    }
1819    xc_dasm_string(return_value, code TSRMLS_CC);
1820}
1821/* }}} */
1822#endif
1823/* {{{ proto string xcache_encode(string filename)
1824   Encode php file into XCache opcode encoded format */
1825#ifdef HAVE_XCACHE_ENCODER
1826PHP_FUNCTION(xcache_encode)
1827{
1828}
1829#endif
1830/* }}} */
1831/* {{{ proto bool xcache_decode_file(string filename)
1832   Decode(load) opcode from XCache encoded format file */
1833#ifdef HAVE_XCACHE_DECODER
1834PHP_FUNCTION(xcache_decode_file)
1835{
1836}
1837#endif
1838/* }}} */
1839/* {{{ proto bool xcache_decode_string(string data)
1840   Decode(load) opcode from XCache encoded format data */
1841#ifdef HAVE_XCACHE_DECODER
1842PHP_FUNCTION(xcache_decode_string)
1843{
1844}
1845#endif
1846/* }}} */
1847/* {{{ xc_call_getter */
1848typedef const char *(xc_name_getter_t)(zend_uchar type);
1849static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
1850{
1851    long spec;
1852    const char *name;
1853
1854    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
1855        return;
1856    }
1857    if (spec >= 0 && spec < count) {
1858        name = getter((zend_uchar) spec);
1859        if (name) {
1860            /* RETURN_STRING */
1861            int len = strlen(name);
1862            return_value->value.str.len = len;
1863            return_value->value.str.val = estrndup(name, len);
1864            return_value->type = IS_STRING; 
1865            return;
1866        }
1867    }
1868    RETURN_NULL();
1869}
1870/* }}} */
1871/* {{{ proto string xcache_get_op_type(int op_type) */
1872PHP_FUNCTION(xcache_get_op_type)
1873{
1874    xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1875}
1876/* }}} */
1877/* {{{ proto string xcache_get_data_type(int type) */
1878PHP_FUNCTION(xcache_get_data_type)
1879{
1880    xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1881}
1882/* }}} */
1883/* {{{ proto string xcache_get_opcode(int opcode) */
1884PHP_FUNCTION(xcache_get_opcode)
1885{
1886    xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1887}
1888/* }}} */
1889/* {{{ proto string xcache_get_op_spec(int op_type) */
1890PHP_FUNCTION(xcache_get_op_spec)
1891{
1892    xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
1893}
1894/* }}} */
1895#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
1896/* {{{ proto string xcache_get_opcode_spec(int opcode) */
1897PHP_FUNCTION(xcache_get_opcode_spec)
1898{
1899    long spec;
1900    const xc_opcode_spec_t *opspec;
1901
1902    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
1903        return;
1904    }
1905    if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
1906        opspec = xc_get_opcode_spec((zend_uchar) spec);
1907        if (opspec) {
1908            array_init(return_value);
1909            add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
1910            add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
1911            add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
1912            add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
1913            return;
1914        }
1915    }
1916    RETURN_NULL();
1917}
1918/* }}} */
1919#endif
1920/* {{{ proto mixed xcache_get_special_value(zval value) */
1921PHP_FUNCTION(xcache_get_special_value)
1922{
1923    zval *value;
1924
1925    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
1926        return;
1927    }
1928
1929    if (value->type == IS_CONSTANT) {
1930        *return_value = *value;
1931        zval_copy_ctor(return_value);
1932        return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
1933        return;
1934    }
1935
1936    if (value->type == IS_CONSTANT_ARRAY) {
1937        *return_value = *value;
1938        zval_copy_ctor(return_value);
1939        return_value->type = IS_ARRAY;
1940        return;
1941    }
1942
1943    RETURN_NULL();
1944}
1945/* }}} */
1946/* {{{ proto string xcache_coredump(int op_type) */
1947PHP_FUNCTION(xcache_coredump)
1948{
1949    if (xc_test) {
1950        raise(SIGSEGV);
1951    }
1952    else {
1953        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
1954    }
1955}
1956/* }}} */
1957/* {{{ proto string xcache_is_autoglobal(string name) */
1958PHP_FUNCTION(xcache_is_autoglobal)
1959{
1960    char *name;
1961    int name_len;
1962
1963    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
1964        return;
1965    }
1966
1967    RETURN_BOOL(zend_hash_exists(CG(auto_globals), name, name_len + 1));
1968}
1969/* }}} */
1970static function_entry xcache_functions[] = /* {{{ */
1971{
1972    PHP_FE(xcache_count,             NULL)
1973    PHP_FE(xcache_info,              NULL)
1974    PHP_FE(xcache_list,              NULL)
1975    PHP_FE(xcache_clear_cache,       NULL)
1976    PHP_FE(xcache_coredump,          NULL)
1977#ifdef HAVE_XCACHE_ASSEMBLER
1978    PHP_FE(xcache_asm,               NULL)
1979#endif
1980#ifdef HAVE_XCACHE_DISASSEMBLER
1981    PHP_FE(xcache_dasm_file,         NULL)
1982    PHP_FE(xcache_dasm_string,       NULL)
1983#endif
1984#ifdef HAVE_XCACHE_ENCODER
1985    PHP_FE(xcache_encode,            NULL)
1986#endif
1987#ifdef HAVE_XCACHE_DECODER
1988    PHP_FE(xcache_decode_file,       NULL)
1989    PHP_FE(xcache_decode_string,     NULL)
1990#endif
1991#ifdef HAVE_XCACHE_COVERAGER
1992    PHP_FE(xcache_coverager_decode,  NULL)
1993#endif
1994    PHP_FE(xcache_get_special_value, NULL)
1995    PHP_FE(xcache_get_op_type,       NULL)
1996    PHP_FE(xcache_get_data_type,     NULL)
1997    PHP_FE(xcache_get_opcode,        NULL)
1998#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
1999    PHP_FE(xcache_get_opcode_spec,   NULL)
2000#endif
2001    PHP_FE(xcache_is_autoglobal,     NULL)
2002    PHP_FE(xcache_inc,               NULL)
2003    PHP_FE(xcache_dec,               NULL)
2004    PHP_FE(xcache_get,               NULL)
2005    PHP_FE(xcache_set,               NULL)
2006    PHP_FE(xcache_isset,             NULL)
2007    PHP_FE(xcache_unset,             NULL)
2008    {NULL, NULL,                     NULL}
2009};
2010/* }}} */
2011
2012/* old signal handlers {{{ */
2013typedef void (*xc_sighandler_t)(int);
2014#define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
2015#include "foreachcoresig.h"
2016#undef FOREACH_SIG
2017/* }}} */
2018static void xcache_signal_handler(int sig);
2019static void xcache_restore_signal_handler() /* {{{ */
2020{
2021#define FOREACH_SIG(sig) do { \
2022    if (old_##sig##_handler != xcache_signal_handler) { \
2023        signal(sig, old_##sig##_handler); \
2024    } \
2025    else { \
2026        signal(sig, SIG_DFL); \
2027    } \
2028} while (0)
2029#include "foreachcoresig.h"
2030#undef FOREACH_SIG
2031}
2032/* }}} */
2033static void xcache_init_signal_handler() /* {{{ */
2034{
2035#define FOREACH_SIG(sig) \
2036    old_##sig##_handler = signal(sig, xcache_signal_handler)
2037#include "foreachcoresig.h"
2038#undef FOREACH_SIG
2039}
2040/* }}} */
2041static void xcache_signal_handler(int sig) /* {{{ */
2042{
2043    xcache_restore_signal_handler();
2044    if (xc_coredump_dir && xc_coredump_dir[0]) {
2045        chdir(xc_coredump_dir);
2046    }
2047    raise(sig);
2048}
2049/* }}} */
2050
2051/* {{{ PHP_INI */
2052
2053static PHP_INI_MH(xc_OnUpdateBool)
2054{
2055    zend_bool *p = (zend_bool *)mh_arg1;
2056
2057    if (strncasecmp("on", new_value, sizeof("on"))) {
2058        *p = (zend_bool) atoi(new_value);
2059    }
2060    else {
2061        *p = (zend_bool) 1;
2062    }
2063    return SUCCESS;
2064}
2065
2066static PHP_INI_MH(xc_OnUpdateString)
2067{
2068    char **p = (char**)mh_arg1;
2069    if (*p) {
2070        pefree(*p, 1);
2071    }
2072    *p = pemalloc(strlen(new_value) + 1, 1);
2073    strcpy(*p, new_value);
2074    return SUCCESS;
2075}
2076
2077#ifndef ZEND_ENGINE_2
2078#define OnUpdateLong OnUpdateInt
2079#endif
2080
2081#ifdef ZEND_WIN32
2082#   define DEFAULT_PATH "xcache"
2083#else
2084#   define DEFAULT_PATH "/dev/zero"
2085#endif
2086PHP_INI_BEGIN()
2087    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_mmap_path)
2088    PHP_INI_ENTRY1     ("xcache.coredump_directory",      "", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coredump_dir)
2089    PHP_INI_ENTRY1     ("xcache.test",                   "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_test)
2090    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_readonly_protection)
2091
2092    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,        cacher,            zend_xcache_globals, xcache_globals)
2093#ifdef HAVE_XCACHE_OPTIMIZER
2094    STD_PHP_INI_BOOLEAN("xcache.optimizer",              "0", PHP_INI_ALL,    OnUpdateBool,        optimizer,         zend_xcache_globals, xcache_globals)
2095#endif
2096    STD_PHP_INI_ENTRY  ("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong,        var_ttl,           zend_xcache_globals, xcache_globals)
2097#ifdef HAVE_XCACHE_COVERAGER
2098    PHP_INI_ENTRY1     ("xcache.coveragedump_directory", "/tmp/pcov/", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coveragedump_dir)
2099    STD_PHP_INI_BOOLEAN("xcache.coveragedumper" ,                 "0", PHP_INI_ALL,    OnUpdateBool,        coveragedumper,    zend_xcache_globals, xcache_globals)
2100#endif
2101PHP_INI_END()
2102/* }}} */
2103static int xc_config_string_disp(char *name, char *default_value) /* {{{ */
2104{
2105    char *value;
2106    char buf[100];
2107
2108    if (cfg_get_string(name, &value) != SUCCESS) {
2109        sprintf(buf, "%s (default)", default_value);
2110        php_info_print_table_row(2, name, buf);
2111    }
2112    else {
2113        php_info_print_table_row(2, name, value);
2114    }
2115
2116    return SUCCESS;
2117}
2118/* }}} */
2119#define xc_config_hash_disp xc_config_string_disp
2120#define xc_config_long_disp xc_config_string_disp
2121/* {{{ PHP_MINFO_FUNCTION(xcache) */
2122static PHP_MINFO_FUNCTION(xcache)
2123{
2124    char buf[100];
2125    char *ptr;
2126
2127    php_info_print_table_start();
2128    php_info_print_table_header(2, "XCache Support", "enabled");
2129    php_info_print_table_row(2, "Version", XCACHE_VERSION);
2130    php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
2131    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A");
2132
2133    if (xc_php_size) {
2134        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
2135        sprintf(buf, "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
2136        php_info_print_table_row(2, "Opcode Cache", buf);
2137        efree(ptr);
2138    }
2139    else {
2140        php_info_print_table_row(2, "Opcode Cache", "disabled");
2141    }
2142    if (xc_var_size) {
2143        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
2144        sprintf(buf, "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
2145        php_info_print_table_row(2, "Variable Cache", buf);
2146        efree(ptr);
2147    }
2148    else {
2149        php_info_print_table_row(2, "Variable Cache", "disabled");
2150    }
2151#ifdef HAVE_XCACHE_COVERAGER
2152    php_info_print_table_row(2, "Coverage Dumper", XG(coveragedumper) && xc_coveragedump_dir && xc_coveragedump_dir[0] ? "enabled" : "disabled");
2153#endif
2154    php_info_print_table_end();
2155
2156    php_info_print_table_start();
2157    php_info_print_table_header(2, "Directive ", "Value");
2158    xc_config_string_disp("xcache.shm_scheme", "mmap");
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/* }}} */
2240static int xc_config_string(char **p, char *name, char *default_value) /* {{{ */
2241{
2242    char *value;
2243
2244    if (cfg_get_string(name, &value) != SUCCESS) {
2245        value = default_value;
2246    }
2247
2248    *p = strdup(value);
2249    return SUCCESS;
2250}
2251/* }}} */
2252/* {{{ PHP_MINIT_FUNCTION(xcache) */
2253static PHP_MINIT_FUNCTION(xcache)
2254{
2255    char *env;
2256
2257    xc_module_gotup = 1;
2258    if (!xc_zend_extension_gotup) {
2259        if (zend_get_extension(XCACHE_NAME) == NULL) {
2260            xc_zend_extension_register(&zend_extension_entry, 0);
2261            xc_zend_extension_startup(&zend_extension_entry);
2262        }
2263    }
2264
2265#ifndef PHP_GINIT
2266    ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
2267#endif
2268    REGISTER_INI_ENTRIES();
2269
2270    if (strcmp(sapi_module.name, "cli") == 0) {
2271        if ((env = getenv("XCACHE_TEST")) != NULL) {
2272            zend_alter_ini_entry("xcache.test", sizeof("xcache.test"), env, strlen(env) + 1, PHP_INI_SYSTEM, PHP_INI_STAGE_STARTUP);
2273        }
2274        if (!xc_test) {
2275            /* disable cache for cli except for test */
2276            xc_php_size = xc_var_size = 0;
2277        }
2278    }
2279
2280    xc_config_string(&xc_shm_scheme,   "xcache.shm_scheme", "mmap");
2281    xc_config_long(&xc_php_size,       "xcache.size",        "0");
2282    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
2283    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
2284    xc_config_long(&xc_php_ttl,        "xcache.ttl",         "0");
2285    xc_config_long(&xc_php_gc_interval, "xcache.gc_interval", "0");
2286
2287    xc_config_long(&xc_var_size,       "xcache.var_size",          "0");
2288    xc_config_hash(&xc_var_hcache,     "xcache.var_count",         "1");
2289    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",        "8K");
2290    xc_config_long(&xc_var_maxttl,     "xcache.var_maxttl",        "0");
2291    xc_config_long(&xc_var_gc_interval, "xcache.var_gc_interval", "120");
2292
2293    if (xc_php_size <= 0) {
2294        xc_php_size = xc_php_hcache.size = 0;
2295    }
2296    if (xc_var_size <= 0) {
2297        xc_var_size = xc_var_hcache.size = 0;
2298    }
2299
2300    if (xc_coredump_dir && xc_coredump_dir[0]) {
2301        xcache_init_signal_handler();
2302    }
2303
2304    xc_init_constant(module_number TSRMLS_CC);
2305    xc_shm_init_modules();
2306
2307    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
2308        if (!xc_init(module_number TSRMLS_CC)) {
2309            zend_error(E_ERROR, "XCache: Cannot init");
2310            goto err_init;
2311        }
2312        xc_initized = 1;
2313    }
2314
2315#ifdef HAVE_XCACHE_COVERAGER
2316    xc_coverager_init(module_number TSRMLS_CC);
2317#endif
2318
2319    return SUCCESS;
2320
2321err_init:
2322    return FAILURE;
2323}
2324/* }}} */
2325/* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
2326static PHP_MSHUTDOWN_FUNCTION(xcache)
2327{
2328    if (xc_initized) {
2329        xc_destroy();
2330        xc_initized = 0;
2331    }
2332    if (xc_mmap_path) {
2333        pefree(xc_mmap_path, 1);
2334        xc_mmap_path = NULL;
2335    }
2336    if (xc_shm_scheme) {
2337        pefree(xc_shm_scheme, 1);
2338        xc_shm_scheme = NULL;
2339    }
2340
2341#ifdef HAVE_XCACHE_COVERAGER
2342    xc_coverager_destroy();
2343#endif
2344
2345    if (xc_coredump_dir && xc_coredump_dir[0]) {
2346        xcache_restore_signal_handler();
2347    }
2348    if (xc_coredump_dir) {
2349        pefree(xc_coredump_dir, 1);
2350        xc_coredump_dir = NULL;
2351    }
2352#ifndef PHP_GINIT
2353#   ifdef ZTS
2354    ts_free_id(xcache_globals_id);
2355#   else
2356    xc_shutdown_globals(&xcache_globals TSRMLS_CC);
2357#   endif
2358#endif
2359
2360    UNREGISTER_INI_ENTRIES();
2361    return SUCCESS;
2362}
2363/* }}} */
2364/* {{{ PHP_RINIT_FUNCTION(xcache) */
2365static PHP_RINIT_FUNCTION(xcache)
2366{
2367    xc_request_init(TSRMLS_C);
2368    return SUCCESS;
2369}
2370/* }}} */
2371/* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
2372#ifndef ZEND_ENGINE_2
2373static PHP_RSHUTDOWN_FUNCTION(xcache)
2374#else
2375static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
2376#endif
2377{
2378#ifdef ZEND_ENGINE_2
2379    TSRMLS_FETCH();
2380#endif
2381
2382    xc_request_shutdown(TSRMLS_C);
2383    return SUCCESS;
2384}
2385/* }}} */
2386/* {{{ module definition structure */
2387
2388zend_module_entry xcache_module_entry = {
2389    STANDARD_MODULE_HEADER,
2390    "XCache",
2391    xcache_functions,
2392    PHP_MINIT(xcache),
2393    PHP_MSHUTDOWN(xcache),
2394    PHP_RINIT(xcache),
2395#ifndef ZEND_ENGINE_2
2396    PHP_RSHUTDOWN(xcache),
2397#else
2398    NULL,
2399#endif
2400    PHP_MINFO(xcache),
2401    XCACHE_VERSION,
2402#ifdef PHP_GINIT
2403    PHP_MODULE_GLOBALS(xcache),
2404    PHP_GINIT(xcache),
2405    PHP_GSHUTDOWN(xcache),
2406#endif
2407#ifdef ZEND_ENGINE_2
2408    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
2409#else
2410    NULL,
2411    NULL,
2412#endif
2413    STANDARD_MODULE_PROPERTIES_EX
2414};
2415
2416#ifdef COMPILE_DL_XCACHE
2417ZEND_GET_MODULE(xcache)
2418#endif
2419/* }}} */
2420ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
2421{
2422    if (xc_zend_extension_gotup) {
2423        return FAILURE;
2424    }
2425    xc_zend_extension_gotup = 1;
2426    if (!xc_module_gotup) {
2427        return zend_startup_module(&xcache_module_entry);
2428    }
2429    return SUCCESS;
2430}
2431/* }}} */
2432ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
2433{
2434    /* empty */
2435}
2436/* }}} */
2437ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
2438{
2439#ifdef HAVE_XCACHE_COVERAGER
2440    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
2441#endif
2442}
2443/* }}} */
2444ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
2445{
2446#if 0
2447    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
2448#endif
2449}
2450/* }}} */
2451ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
2452{
2453#if 0
2454    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
2455#endif
2456}
2457/* }}} */
2458/* {{{ zend extension definition structure */
2459ZEND_DLEXPORT zend_extension zend_extension_entry = {
2460    XCACHE_NAME,
2461    XCACHE_VERSION,
2462    XCACHE_AUTHOR,
2463    XCACHE_URL,
2464    XCACHE_COPYRIGHT,
2465    xcache_zend_startup,
2466    xcache_zend_shutdown,
2467    NULL,           /* activate_func_t */
2468    NULL,           /* deactivate_func_t */
2469    NULL,           /* message_handler_func_t */
2470    NULL,           /* op_array_handler_func_t */
2471    xcache_statement_handler,
2472    xcache_fcall_begin_handler,
2473    xcache_fcall_end_handler,
2474    NULL,           /* op_array_ctor_func_t */
2475    NULL,           /* op_array_dtor_func_t */
2476    STANDARD_ZEND_EXTENSION_PROPERTIES
2477};
2478
2479#ifndef ZEND_EXT_API
2480#   define ZEND_EXT_API ZEND_DLEXPORT
2481#endif
2482#if COMPILE_DL_XCACHE
2483ZEND_EXTENSION();
2484#endif
2485/* }}} */
Note: See TracBrowser for help on using the repository browser.