source: trunk/xcache.c @ 136

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

update last_gc_deletes so it gc by interval

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