source: trunk/xcache.c @ 118

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

show gc countdown in admin page

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