source: svn/trunk/xcache.c @ 143

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

fixed string parameter parsing arg type for disassembler functions. thanks to Nuno Lopes' check_parameters.php

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