source: trunk/xcache.c @ 164

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

revert most of [82] back and use dummy ptr

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