source: trunk/xcache.c @ 119

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

fix leak in xcache.test=1; XCACHE_VERSION/XCACHE_MODULES length off-by-one

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