source: trunk/xcache.c @ 320

Last change on this file since 320 was 320, checked in by moo, 7 years ago

cacher: disable extra openbase dir warnning

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