source: trunk/xcache.c @ 317

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

cacher: fix stat and skip user stream

File size: 72.2 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
859    stream = php_stream_open_wrapper(xce->name.str.val, "rb", REPORT_ERRORS | ENFORCE_SAFE_MODE, NULL);
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
[314]1260        xce.data.php = &php;
[311]1261        if (catched) {
[314]1262            goto err_aftersandbox;
[311]1263        }
1264    }
1265    /* }}} */
1266#ifdef HAVE_INODE
1267    /* {{{ path name fix
1268     * inode enabled entry hash/compare on name
1269     * do not update to its name to real pathname
1270     * WARNING: this code is required to be after compile
1271     */
1272    if (xce.inode) {
1273        filename = h->opened_path ? h->opened_path : h->filename;
1274        if (xce.name.str.val != filename) {
1275            xce.name.str.val = filename;
1276            xce.name.str.len = strlen(filename);
1277        }
1278    }
1279    /* }}} */
1280#endif
1281#ifdef SHOW_DPRINT
1282    xc_dprint(&xce, 0 TSRMLS_CC);
1283#endif
1284    stored_xce = NULL;
1285    ENTER_LOCK_EX(cache) { /* {{{ php_store/entry_store */
1286        /* php_store */
[314]1287        if (newlycompiled) {
[311]1288            stored_php = xc_php_store_dmz(&php TSRMLS_CC);
1289            /* error */
1290            if (!stored_php) {
1291                break;
1292            }
1293        }
1294        /* entry_store */
1295        stored_xce = xc_entry_store_dmz(&xce TSRMLS_CC);
1296        if (stored_xce) {
1297            stored_xce->data.php = stored_php;
1298            stored_php->refcount ++;
1299        }
1300        else {
1301            /* error */
1302            xc_php_remove_dmz(stored_php);
1303            stored_php = NULL;
1304        }
1305    } LEAVE_LOCK_EX(cache);
1306    /* }}} */
1307    TRACE("%s", stored_xce ? "stored" : "store failed");
1308
1309    cache->compiling = 0;
1310    if (catched) {
[314]1311        goto err_aftersandbox;
[311]1312    }
1313
[314]1314    xc_free_php(&php TSRMLS_CC);
1315    xc_sandbox_free(&sandbox, 0 TSRMLS_CC);
1316
[311]1317    if (stored_xce) {
1318        if (op_array) {
1319#ifdef ZEND_ENGINE_2
1320            destroy_op_array(op_array TSRMLS_CC);
1321#else
1322            destroy_op_array(op_array);
1323#endif
1324            efree(op_array);
1325            h = NULL;
1326        }
1327        return xc_compile_restore(stored_xce, h TSRMLS_CC);
1328    }
1329    return op_array;
[314]1330
1331err_aftersandbox:
1332    xc_free_php(&php TSRMLS_CC);
1333    xc_sandbox_free(&sandbox, 0 TSRMLS_CC);
1334
1335    if (catched) {
1336        cache->compiling = 0;
1337        zend_bailout();
1338    }
1339    return op_array;
[311]1340}
1341/* }}} */
1342
[1]1343/* gdb helper functions, but N/A for coredump */
1344int xc_is_rw(const void *p) /* {{{ */
1345{
[148]1346    xc_shm_t *shm;
[1]1347    int i;
1348    if (!xc_initized) {
1349        return 0;
1350    }
1351    for (i = 0; i < xc_php_hcache.size; i ++) {
[148]1352        shm = xc_php_caches[i]->shm;
1353        if (shm->handlers->is_readwrite(shm, p)) {
[1]1354            return 1;
1355        }
1356    }
1357    for (i = 0; i < xc_var_hcache.size; i ++) {
[148]1358        shm = xc_var_caches[i]->shm;
1359        if (shm->handlers->is_readwrite(shm, p)) {
[1]1360            return 1;
1361        }
1362    }
1363    return 0;
1364}
1365/* }}} */
1366int xc_is_ro(const void *p) /* {{{ */
1367{
[148]1368    xc_shm_t *shm;
[1]1369    int i;
1370    if (!xc_initized) {
1371        return 0;
1372    }
1373    for (i = 0; i < xc_php_hcache.size; i ++) {
[148]1374        shm = xc_php_caches[i]->shm;
1375        if (shm->handlers->is_readonly(shm, p)) {
[1]1376            return 1;
1377        }
1378    }
1379    for (i = 0; i < xc_var_hcache.size; i ++) {
[148]1380        shm = xc_var_caches[i]->shm;
1381        if (shm->handlers->is_readonly(shm, p)) {
[1]1382            return 1;
1383        }
1384    }
1385    return 0;
1386}
1387/* }}} */
1388int xc_is_shm(const void *p) /* {{{ */
1389{
1390    return xc_is_ro(p) || xc_is_rw(p);
1391}
1392/* }}} */
1393
1394/* module helper function */
1395static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
1396{
[11]1397    typedef struct {
[1]1398        const char *prefix;
[20]1399        zend_uchar (*getsize)();
[1]1400        const char *(*get)(zend_uchar i);
[11]1401    } xc_meminfo_t;
1402    xc_meminfo_t nameinfos[] = {
[1]1403        { "",        xc_get_op_type_count,   xc_get_op_type   },
1404        { "",        xc_get_data_type_count, xc_get_data_type },
1405        { "",        xc_get_opcode_count,    xc_get_opcode    },
1406        { "OPSPEC_", xc_get_op_spec_count,   xc_get_op_spec   },
1407        { NULL, NULL, NULL }
1408    };
[11]1409    xc_meminfo_t* p;
[20]1410    zend_uchar i, count;
[1]1411    char const_name[96];
1412    int const_name_len;
1413    int undefdone = 0;
1414
1415    for (p = nameinfos; p->getsize; p ++) {
[20]1416        count = p->getsize();
1417        for (i = 0; i < count; i ++) {
[1]1418            const char *name = p->get(i);
1419            if (!name) continue;
1420            if (strcmp(name, "UNDEF") == 0) {
1421                if (undefdone) continue;
1422                undefdone = 1;
1423            }
1424            const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
1425            zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1426        }
1427    }
1428
1429    zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1430    zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1431    zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
[119]1432    zend_register_stringl_constant(ZEND_STRS("XCACHE_VERSION"), ZEND_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
1433    zend_register_stringl_constant(ZEND_STRS("XCACHE_MODULES"), ZEND_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
[1]1434    return 0;
1435}
1436/* }}} */
[11]1437static xc_shm_t *xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache) /* {{{ */
[1]1438{
1439    int i;
1440    xc_cache_t *cache;
1441    xc_shm_t *shm;
1442
1443    if (!caches) {
1444        return NULL;
1445    }
1446    shm = NULL;
1447    for (i = 0; i < hcache->size; i ++) {
1448        cache = caches[i];
1449        if (cache) {
1450            if (cache->lck) {
1451                xc_lock_destroy(cache->lck);
1452            }
1453            /* do NOT free
1454            if (cache->entries) {
[148]1455                cache->mem->handlers->free(cache->mem, cache->entries);
[1]1456            }
[148]1457            cache->mem->handlers->free(cache->mem, cache);
[1]1458            */
[154]1459            shm = cache->shm;
[148]1460            shm->handlers->memdestroy(cache->mem);
[1]1461        }
1462    }
1463    free(caches);
1464    return shm;
1465}
1466/* }}} */
[311]1467static 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]1468{
1469    xc_cache_t **caches = NULL, *cache;
1470    xc_mem_t *mem;
[114]1471    time_t now = time(NULL);
[1]1472    int i;
[49]1473    xc_memsize_t memsize;
[11]1474
[49]1475    memsize = shmsize / hcache->size;
[1]1476
[49]1477    /* Don't let it break out of mem after ALIGNed
1478     * This is important for
1479     * Simply loop until it fit our need
1480     */
1481    while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
1482        if (memsize < ALIGN(1)) {
1483            CHECK(NULL, "cache too small");
1484        }
1485        memsize --;
1486    }
1487
[1]1488    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
1489
1490    for (i = 0; i < hcache->size; i ++) {
[148]1491        CHECK(mem            = shm->handlers->meminit(shm, memsize), "Failed init memory allocator");
1492        CHECK(cache          = mem->handlers->calloc(mem, 1, sizeof(xc_cache_t)), "cache OOM");
1493        CHECK(cache->entries = mem->handlers->calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
[311]1494        if (hphp) {
1495            CHECK(cache->phps= mem->handlers->calloc(mem, hphp->size, sizeof(xc_entry_data_php_t*)), "phps OOM");
1496        }
[1]1497        CHECK(cache->lck     = xc_lock_init(NULL), "can't create lock");
1498
1499        cache->hcache  = hcache;
1500        cache->hentry  = hentry;
[311]1501        cache->hphp    = hphp;
[1]1502        cache->shm     = shm;
1503        cache->mem     = mem;
1504        cache->cacheid = i;
[114]1505        cache->last_gc_deletes = now;
1506        cache->last_gc_expires = now;
[1]1507        caches[i] = cache;
1508    }
1509    return caches;
1510
1511err:
1512    if (caches) {
1513        xc_cache_destroy(caches, hcache);
1514    }
1515    return NULL;
1516}
1517/* }}} */
1518static void xc_destroy() /* {{{ */
1519{
1520    xc_shm_t *shm = NULL;
1521
1522    if (origin_compile_file) {
1523        zend_compile_file = origin_compile_file;
1524        origin_compile_file = NULL;
1525    }
1526
1527    if (xc_php_caches) {
1528        shm = xc_cache_destroy(xc_php_caches, &xc_php_hcache);
1529        xc_php_caches = NULL;
1530    }
1531    if (xc_var_caches) {
1532        shm = xc_cache_destroy(xc_var_caches, &xc_var_hcache);
1533        xc_var_caches = NULL;
1534    }
1535    if (shm) {
1536        xc_shm_destroy(shm);
1537    }
1538}
1539/* }}} */
1540static int xc_init(int module_number TSRMLS_DC) /* {{{ */
1541{
1542    xc_shm_t *shm;
1543
[11]1544    xc_php_caches = xc_var_caches = NULL;
1545
[1]1546    if (xc_php_size || xc_var_size) {
[148]1547        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");
1548        if (!shm->handlers->can_readonly(shm)) {
[1]1549            xc_readonly_protection = 0;
1550        }
1551
1552        if (xc_php_size) {
1553            origin_compile_file = zend_compile_file;
1554            zend_compile_file = xc_compile_file;
1555
[311]1556            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]1557        }
1558
1559        if (xc_var_size) {
[311]1560            CHECK(xc_var_caches = xc_cache_init(shm, &xc_var_hcache, &xc_var_hentry, NULL, xc_var_size), "failed init variable cache");
[1]1561        }
1562    }
1563    return 1;
1564
1565err:
1566    if (xc_php_caches || xc_var_caches) {
1567        xc_destroy();
1568        /* shm destroied */
1569    }
1570    else if (shm) {
1571        xc_shm_destroy(shm);
1572    }
1573    return 0;
1574}
1575/* }}} */
1576static void xc_request_init(TSRMLS_D) /* {{{ */
1577{
[17]1578    int i;
1579
1580    if (xc_php_hcache.size && !XG(php_holds)) {
1581        XG(php_holds) = calloc(xc_php_hcache.size, sizeof(xc_stack_t));
1582        for (i = 0; i < xc_php_hcache.size; i ++) {
1583            xc_stack_init(&XG(php_holds[i]));
1584        }
1585    }
1586
1587    if (xc_var_hcache.size && !XG(var_holds)) {
1588        XG(var_holds) = calloc(xc_var_hcache.size, sizeof(xc_stack_t));
1589        for (i = 0; i < xc_var_hcache.size; i ++) {
1590            xc_stack_init(&XG(var_holds[i]));
1591        }
1592    }
1593
[1]1594    if (XG(cacher)) {
1595#if PHP_API_VERSION <= 20041225
1596        XG(request_time) = time(NULL);
1597#else
1598        XG(request_time) = sapi_get_request_time(TSRMLS_C);
1599#endif
1600    }
[27]1601#ifdef HAVE_XCACHE_COVERAGER
1602    xc_coverager_request_init(TSRMLS_C);
[1]1603#endif
1604}
1605/* }}} */
1606static void xc_request_shutdown(TSRMLS_D) /* {{{ */
1607{
1608    xc_entry_unholds(TSRMLS_C);
[120]1609    xc_gc_expires_php(TSRMLS_C);
1610    xc_gc_expires_var(TSRMLS_C);
[114]1611    xc_gc_deletes(TSRMLS_C);
[27]1612#ifdef HAVE_XCACHE_COVERAGER
1613    xc_coverager_request_shutdown(TSRMLS_C);
[1]1614#endif
1615}
1616/* }}} */
[92]1617/* {{{ PHP_GINIT_FUNCTION(xcache) */
1618static
1619#ifdef PHP_GINIT_FUNCTION
1620PHP_GINIT_FUNCTION(xcache)
1621#else
1622void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
1623#endif
[1]1624{
[92]1625    memset(xcache_globals, 0, sizeof(zend_xcache_globals));
[1]1626}
1627/* }}} */
[92]1628/* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
1629static
1630#ifdef PHP_GSHUTDOWN_FUNCTION
1631PHP_GSHUTDOWN_FUNCTION(xcache)
1632#else
1633void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
1634#endif
[1]1635{
1636    int i;
1637
[92]1638    if (xcache_globals->php_holds != NULL) {
[1]1639        for (i = 0; i < xc_php_hcache.size; i ++) {
[92]1640            xc_stack_destroy(&xcache_globals->php_holds[i]);
[1]1641        }
[92]1642        free(xcache_globals->php_holds);
1643        xcache_globals->php_holds = NULL;
[1]1644    }
1645
[92]1646    if (xcache_globals->var_holds != NULL) {
[1]1647        for (i = 0; i < xc_var_hcache.size; i ++) {
[92]1648            xc_stack_destroy(&xcache_globals->var_holds[i]);
[1]1649        }
[92]1650        free(xcache_globals->var_holds);
1651        xcache_globals->var_holds = NULL;
[1]1652    }
1653}
1654/* }}} */
1655
1656/* user functions */
[55]1657static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
[34]1658{
1659    zval **server = NULL;
1660    zval **user = NULL;
1661    zval **pass = NULL;
1662    char *admin_user = NULL;
1663    char *admin_pass = NULL;
1664    HashTable *ht;
1665
1666    if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
1667        admin_user = NULL;
1668    }
1669    if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
1670        admin_pass = NULL;
1671    }
1672
1673    if (admin_user == NULL || admin_pass == NULL) {
1674        php_error_docref(NULL TSRMLS_CC, E_ERROR, "xcache.admin.user and xcache.admin.pass is required");
1675        zend_bailout();
1676    }
1677    if (strlen(admin_pass) != 32) {
1678        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));
1679        zend_bailout();
1680    }
1681
[105]1682#ifdef ZEND_ENGINE_2_1
[113]1683    zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
[105]1684#endif
[34]1685    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
1686        php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
1687        zend_bailout();
1688    }
1689    ht = HASH_OF((*server));
1690
1691    if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
1692        user = NULL;
1693    }
1694    else if (Z_TYPE_PP(user) != IS_STRING) {
1695        user = NULL;
1696    }
1697
1698    if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
1699        pass = NULL;
1700    }
1701    else if (Z_TYPE_PP(pass) != IS_STRING) {
1702        pass = NULL;
1703    }
1704
1705    if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
1706        PHP_MD5_CTX context;
1707        char md5str[33];
1708        unsigned char digest[16];
1709
1710        PHP_MD5Init(&context);
[91]1711        PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
[34]1712        PHP_MD5Final(digest, &context);
1713
1714        md5str[0] = '\0';
1715        make_digest(md5str, digest);
1716        if (strcmp(admin_pass, md5str) == 0) {
1717            return 1;
1718        }
1719    }
1720
1721#define STR "WWW-authenticate: basic realm='XCache Administration'"
1722    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1723#undef STR
1724#define STR "HTTP/1.0 401 Unauthorized"
1725    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
1726#undef STR
1727    ZEND_PUTS("XCache Auth Failed. User and Password is case sense\n");
1728
1729    zend_bailout();
1730    return 0;
1731}
1732/* }}} */
1733/* {{{ xcache_admin_operate */
[1]1734typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
[34]1735static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
[1]1736{
1737    long type;
1738    int size;
1739    xc_cache_t **caches, *cache;
1740    long id = 0;
1741
[279]1742    xcache_admin_auth_check(TSRMLS_C);
1743
[11]1744    if (!xc_initized) {
[279]1745        RETURN_NULL();
[11]1746    }
1747
[1]1748    if (optype == XC_OP_COUNT) {
1749        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
1750            return;
1751        }
1752    }
1753    else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
1754        return;
1755    }
1756
1757    switch (type) {
1758        case XC_TYPE_PHP:
1759            size = xc_php_hcache.size;
1760            caches = xc_php_caches;
1761            break;
1762
1763        case XC_TYPE_VAR:
1764            size = xc_var_hcache.size;
1765            caches = xc_var_caches;
1766            break;
1767
1768        default:
1769            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
1770            RETURN_FALSE;
1771    }
1772
1773    switch (optype) {
1774        case XC_OP_COUNT:
1775            RETURN_LONG(size)
1776            break;
1777
1778        case XC_OP_INFO:
1779        case XC_OP_LIST:
1780            if (id < 0 || id >= size) {
1781                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1782                RETURN_FALSE;
1783            }
1784
1785            array_init(return_value);
1786
1787            cache = caches[id];
1788            ENTER_LOCK(cache) {
1789                if (optype == XC_OP_INFO) {
[118]1790                    xc_fillinfo_dmz(type, cache, return_value TSRMLS_CC);
[1]1791                }
1792                else {
1793                    xc_filllist_dmz(cache, return_value TSRMLS_CC);
1794                }
1795            } LEAVE_LOCK(cache);
1796            break;
1797        case XC_OP_CLEAR:
1798            {
[141]1799                xc_entry_t *e, *next;
[1]1800                int i, c;
1801
1802                if (id < 0 || id >= size) {
1803                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
1804                    RETURN_FALSE;
1805                }
1806
1807                cache = caches[id];
1808                ENTER_LOCK(cache) {
1809                    for (i = 0, c = cache->hentry->size; i < c; i ++) {
[141]1810                        for (e = cache->entries[i]; e; e = next) {
1811                            next = e->next;
[1]1812                            xc_entry_remove_dmz(e TSRMLS_CC);
1813                        }
1814                        cache->entries[i] = NULL;
1815                    }
1816                } LEAVE_LOCK(cache);
[114]1817                xc_gc_deletes(TSRMLS_C);
[1]1818            }
1819            break;
1820
1821        default:
1822            assert(0);
1823    }
1824}
1825/* }}} */
[9]1826/* {{{ proto int xcache_count(int type)
1827   Return count of cache on specified cache type */
[1]1828PHP_FUNCTION(xcache_count)
1829{
[34]1830    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
[1]1831}
1832/* }}} */
[9]1833/* {{{ proto array xcache_info(int type, int id)
1834   Get cache info by id on specified cache type */
[1]1835PHP_FUNCTION(xcache_info)
1836{
[34]1837    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
[1]1838}
1839/* }}} */
[9]1840/* {{{ proto array xcache_list(int type, int id)
1841   Get cache entries list by id on specified cache type */
[1]1842PHP_FUNCTION(xcache_list)
1843{
[34]1844    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
[1]1845}
1846/* }}} */
[9]1847/* {{{ proto array xcache_clear_cache(int type, int id)
1848   Clear cache by id on specified cache type */
[1]1849PHP_FUNCTION(xcache_clear_cache)
1850{
[34]1851    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
[1]1852}
1853/* }}} */
1854
1855static int xc_entry_init_key_var(xc_entry_t *xce, zval *name TSRMLS_DC) /* {{{ */
1856{
1857    xc_hash_value_t hv;
1858    int cacheid;
1859
1860    switch (Z_TYPE_P(name)) {
1861#ifdef IS_UNICODE
1862        case IS_UNICODE:
1863#endif
1864        case IS_STRING:
1865            break;
1866        default:
1867#ifdef IS_UNICODE
1868            convert_to_text(name);
1869#else
1870            convert_to_string(name);
1871#endif
1872    }
1873#ifdef IS_UNICODE
1874    xce->name_type = name->type;
1875#endif
1876    xce->name = name->value;
1877
[152]1878    hv = xc_entry_hash_var(xce TSRMLS_CC);
[1]1879
1880    cacheid = (hv & xc_var_hcache.mask);
1881    xce->cache = xc_var_caches[cacheid];
1882    hv >>= xc_var_hcache.bits;
1883    xce->hvalue = (hv & xc_var_hentry.mask);
1884
1885    xce->type = XC_TYPE_VAR;
1886    return SUCCESS;
1887}
1888/* }}} */
[9]1889/* {{{ proto mixed xcache_get(string name)
1890   Get cached data by specified name */
[1]1891PHP_FUNCTION(xcache_get)
1892{
1893    xc_entry_t xce, *stored_xce;
1894    xc_entry_data_var_t var;
1895    zval *name;
1896
1897    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1898        return;
1899    }
1900    xce.data.var = &var;
1901    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1902
1903    ENTER_LOCK(xce.cache) {
1904        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1905        if (stored_xce) {
[254]1906            if (!VAR_ENTRY_EXPIRED(stored_xce)) {
[311]1907                xc_processor_restore_zval(return_value, stored_xce->data.var->value, stored_xce->data.var->have_references TSRMLS_CC);
[1]1908                /* return */
1909                break;
1910            }
1911            else {
[11]1912                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]1913            }
1914        }
1915
1916        RETVAL_NULL();
1917    } LEAVE_LOCK(xce.cache);
1918}
1919/* }}} */
[9]1920/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
1921   Store data to cache by specified name */
[1]1922PHP_FUNCTION(xcache_set)
1923{
1924    xc_entry_t xce, *stored_xce;
1925    xc_entry_data_var_t var;
1926    zval *name;
1927    zval *value;
1928
[114]1929    xce.ttl = XG(var_ttl);
1930    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &xce.ttl) == FAILURE) {
[1]1931        return;
1932    }
[114]1933
1934    /* max ttl */
1935    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
1936        xce.ttl = xc_var_maxttl;
1937    }
1938
[1]1939    xce.data.var = &var;
1940    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1941
1942    ENTER_LOCK(xce.cache) {
1943        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1944        if (stored_xce) {
[11]1945            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]1946        }
1947        var.value = value;
[11]1948        RETVAL_BOOL(xc_entry_store_dmz(&xce TSRMLS_CC) != NULL ? 1 : 0);
[1]1949    } LEAVE_LOCK(xce.cache);
1950}
1951/* }}} */
[9]1952/* {{{ proto bool  xcache_isset(string name)
1953   Check if an entry exists in cache by specified name */
[1]1954PHP_FUNCTION(xcache_isset)
1955{
1956    xc_entry_t xce, *stored_xce;
1957    xc_entry_data_var_t var;
1958    zval *name;
1959
1960    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1961        return;
1962    }
1963    xce.data.var = &var;
1964    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1965
1966    ENTER_LOCK(xce.cache) {
1967        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1968        if (stored_xce) {
[126]1969            if (!VAR_ENTRY_EXPIRED(stored_xce)) {
[1]1970                RETVAL_TRUE;
1971                /* return */
1972                break;
1973            }
1974            else {
[11]1975                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]1976            }
1977        }
1978
1979        RETVAL_FALSE;
1980    } LEAVE_LOCK(xce.cache);
1981}
1982/* }}} */
[9]1983/* {{{ proto bool  xcache_unset(string name)
1984   Unset existing data in cache by specified name */
[1]1985PHP_FUNCTION(xcache_unset)
1986{
1987    xc_entry_t xce, *stored_xce;
1988    xc_entry_data_var_t var;
1989    zval *name;
1990
1991    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
1992        return;
1993    }
1994    xce.data.var = &var;
1995    xc_entry_init_key_var(&xce, name TSRMLS_CC);
1996
1997    ENTER_LOCK(xce.cache) {
1998        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
1999        if (stored_xce) {
[11]2000            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]2001            RETVAL_TRUE;
2002        }
2003        else {
2004            RETVAL_FALSE;
2005        }
2006    } LEAVE_LOCK(xce.cache);
2007}
2008/* }}} */
2009static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
2010{
2011    xc_entry_t xce, *stored_xce;
2012    xc_entry_data_var_t var, *stored_var;
2013    zval *name;
2014    long count = 1;
2015    long value = 0;
2016    zval oldzval;
2017
[114]2018    xce.ttl = XG(var_ttl);
2019    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &xce.ttl) == FAILURE) {
[1]2020        return;
2021    }
[114]2022
2023    /* max ttl */
2024    if (xc_var_maxttl && (!xce.ttl || xce.ttl > xc_var_maxttl)) {
2025        xce.ttl = xc_var_maxttl;
2026    }
2027
[1]2028    xce.data.var = &var;
2029    xc_entry_init_key_var(&xce, name TSRMLS_CC);
2030
2031    ENTER_LOCK(xce.cache) {
2032        stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
2033        if (stored_xce) {
[305]2034            TRACE("incdec: gotxce %s", xce.name.str.val);
[1]2035            /* timeout */
[114]2036            if (VAR_ENTRY_EXPIRED(stored_xce)) {
[305]2037                TRACE("%s", "incdec: expired");
[11]2038                xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]2039                stored_xce = NULL;
2040            }
2041            else {
2042                /* do it in place */
[114]2043                stored_var = stored_xce->data.var;
[1]2044                if (Z_TYPE_P(stored_var->value) == IS_LONG) {
[114]2045                    stored_xce->ctime = XG(request_time);
2046                    stored_xce->ttl   = xce.ttl;
[305]2047                    TRACE("%s", "incdec: islong");
[1]2048                    value = Z_LVAL_P(stored_var->value);
2049                    value += (inc == 1 ? count : - count);
2050                    RETVAL_LONG(value);
2051                    Z_LVAL_P(stored_var->value) = value;
[114]2052                    break; /* leave lock */
[1]2053                }
2054                else {
[305]2055                    TRACE("%s", "incdec: notlong");
[311]2056                    xc_processor_restore_zval(&oldzval, stored_xce->data.var->value, stored_xce->data.var->have_references TSRMLS_CC);
[1]2057                    convert_to_long(&oldzval);
2058                    value = Z_LVAL(oldzval);
2059                    zval_dtor(&oldzval);
2060                }
2061            }
2062        }
2063        else {
[305]2064            TRACE("incdec: %s not found", xce.name.str.val);
[1]2065        }
2066
2067        value += (inc == 1 ? count : - count);
2068        RETVAL_LONG(value);
2069        var.value = return_value;
[114]2070
[1]2071        if (stored_xce) {
2072            xce.atime = stored_xce->atime;
2073            xce.ctime = stored_xce->ctime;
2074            xce.hits  = stored_xce->hits;
[11]2075            xc_entry_remove_dmz(stored_xce TSRMLS_CC);
[1]2076        }
[11]2077        xc_entry_store_dmz(&xce TSRMLS_CC);
[1]2078
2079    } LEAVE_LOCK(xce.cache);
2080}
2081/* }}} */
[9]2082/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
2083   Increase an int counter in cache by specified name, create it if not exists */
[1]2084PHP_FUNCTION(xcache_inc)
2085{
2086    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2087}
2088/* }}} */
[9]2089/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
2090   Decrease an int counter in cache by specified name, create it if not exists */
[1]2091PHP_FUNCTION(xcache_dec)
2092{
2093    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2094}
2095/* }}} */
[9]2096/* {{{ proto string xcache_asm(string filename)
2097 */
[1]2098#ifdef HAVE_XCACHE_ASSEMBLER
2099PHP_FUNCTION(xcache_asm)
2100{
2101}
2102#endif
2103/* }}} */
2104#ifdef HAVE_XCACHE_DISASSEMBLER
[9]2105/* {{{ proto array  xcache_dasm_file(string filename)
2106   Disassemble file into opcode array by filename */
[1]2107PHP_FUNCTION(xcache_dasm_file)
2108{
2109    char *filename;
[143]2110    int filename_len;
[1]2111
2112    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
2113        return;
2114    }
2115    if (!filename_len) RETURN_FALSE;
2116
2117    xc_dasm_file(return_value, filename TSRMLS_CC);
2118}
2119/* }}} */
[9]2120/* {{{ proto array  xcache_dasm_string(string code)
2121   Disassemble php code into opcode array */
[1]2122PHP_FUNCTION(xcache_dasm_string)
2123{
2124    zval *code;
2125
2126    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
2127        return;
2128    }
2129    xc_dasm_string(return_value, code TSRMLS_CC);
2130}
2131/* }}} */
2132#endif
[9]2133/* {{{ proto string xcache_encode(string filename)
2134   Encode php file into XCache opcode encoded format */
[1]2135#ifdef HAVE_XCACHE_ENCODER
2136PHP_FUNCTION(xcache_encode)
2137{
2138}
2139#endif
2140/* }}} */
[9]2141/* {{{ proto bool xcache_decode_file(string filename)
2142   Decode(load) opcode from XCache encoded format file */
[1]2143#ifdef HAVE_XCACHE_DECODER
[9]2144PHP_FUNCTION(xcache_decode_file)
[1]2145{
2146}
2147#endif
2148/* }}} */
[9]2149/* {{{ proto bool xcache_decode_string(string data)
2150   Decode(load) opcode from XCache encoded format data */
2151#ifdef HAVE_XCACHE_DECODER
2152PHP_FUNCTION(xcache_decode_string)
2153{
2154}
2155#endif
2156/* }}} */
[1]2157/* {{{ xc_call_getter */
2158typedef const char *(xc_name_getter_t)(zend_uchar type);
2159static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
2160{
2161    long spec;
2162    const char *name;
2163
2164    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
2165        return;
2166    }
2167    if (spec >= 0 && spec < count) {
[11]2168        name = getter((zend_uchar) spec);
[1]2169        if (name) {
2170            /* RETURN_STRING */
2171            int len = strlen(name);
2172            return_value->value.str.len = len;
2173            return_value->value.str.val = estrndup(name, len);
2174            return_value->type = IS_STRING; 
2175            return;
2176        }
2177    }
2178    RETURN_NULL();
2179}
2180/* }}} */
2181/* {{{ proto string xcache_get_op_type(int op_type) */
2182PHP_FUNCTION(xcache_get_op_type)
2183{
2184    xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2185}
2186/* }}} */
2187/* {{{ proto string xcache_get_data_type(int type) */
2188PHP_FUNCTION(xcache_get_data_type)
2189{
2190    xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2191}
2192/* }}} */
2193/* {{{ proto string xcache_get_opcode(int opcode) */
2194PHP_FUNCTION(xcache_get_opcode)
2195{
2196    xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2197}
2198/* }}} */
2199/* {{{ proto string xcache_get_op_spec(int op_type) */
2200PHP_FUNCTION(xcache_get_op_spec)
2201{
2202    xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
2203}
2204/* }}} */
[8]2205#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
[1]2206/* {{{ proto string xcache_get_opcode_spec(int opcode) */
2207PHP_FUNCTION(xcache_get_opcode_spec)
2208{
2209    long spec;
2210    const xc_opcode_spec_t *opspec;
2211
2212    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
2213        return;
2214    }
[17]2215    if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
[11]2216        opspec = xc_get_opcode_spec((zend_uchar) spec);
[1]2217        if (opspec) {
2218            array_init(return_value);
2219            add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
2220            add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
2221            add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
2222            add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
2223            return;
2224        }
2225    }
2226    RETURN_NULL();
2227}
2228/* }}} */
[8]2229#endif
[1]2230/* {{{ proto mixed xcache_get_special_value(zval value) */
2231PHP_FUNCTION(xcache_get_special_value)
2232{
2233    zval *value;
2234
2235    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
2236        return;
2237    }
2238
2239    if (value->type == IS_CONSTANT) {
2240        *return_value = *value;
2241        zval_copy_ctor(return_value);
2242        return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
2243        return;
2244    }
2245
2246    if (value->type == IS_CONSTANT_ARRAY) {
2247        *return_value = *value;
2248        zval_copy_ctor(return_value);
2249        return_value->type = IS_ARRAY;
2250        return;
2251    }
2252
2253    RETURN_NULL();
2254}
2255/* }}} */
2256/* {{{ proto string xcache_coredump(int op_type) */
2257PHP_FUNCTION(xcache_coredump)
2258{
[9]2259    if (xc_test) {
2260        raise(SIGSEGV);
2261    }
2262    else {
2263        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
2264    }
[1]2265}
2266/* }}} */
2267/* {{{ proto string xcache_is_autoglobal(string name) */
2268PHP_FUNCTION(xcache_is_autoglobal)
2269{
2270    char *name;
[143]2271    int name_len;
[1]2272
2273    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
2274        return;
2275    }
2276
2277    RETURN_BOOL(zend_hash_exists(CG(auto_globals), name, name_len + 1));
2278}
2279/* }}} */
2280static function_entry xcache_functions[] = /* {{{ */
2281{
2282    PHP_FE(xcache_count,             NULL)
2283    PHP_FE(xcache_info,              NULL)
2284    PHP_FE(xcache_list,              NULL)
2285    PHP_FE(xcache_clear_cache,       NULL)
2286    PHP_FE(xcache_coredump,          NULL)
2287#ifdef HAVE_XCACHE_ASSEMBLER
2288    PHP_FE(xcache_asm,               NULL)
2289#endif
2290#ifdef HAVE_XCACHE_DISASSEMBLER
2291    PHP_FE(xcache_dasm_file,         NULL)
2292    PHP_FE(xcache_dasm_string,       NULL)
2293#endif
2294#ifdef HAVE_XCACHE_ENCODER
2295    PHP_FE(xcache_encode,            NULL)
2296#endif
2297#ifdef HAVE_XCACHE_DECODER
[9]2298    PHP_FE(xcache_decode_file,       NULL)
2299    PHP_FE(xcache_decode_string,     NULL)
[1]2300#endif
[27]2301#ifdef HAVE_XCACHE_COVERAGER
2302    PHP_FE(xcache_coverager_decode,  NULL)
[204]2303    PHP_FE(xcache_coverager_start,   NULL)
2304    PHP_FE(xcache_coverager_stop,    NULL)
2305    PHP_FE(xcache_coverager_get,     NULL)
[1]2306#endif
2307    PHP_FE(xcache_get_special_value, NULL)
2308    PHP_FE(xcache_get_op_type,       NULL)
2309    PHP_FE(xcache_get_data_type,     NULL)
2310    PHP_FE(xcache_get_opcode,        NULL)
[8]2311#ifdef HAVE_XCACHE_OPCODE_SPEC_DEF
[1]2312    PHP_FE(xcache_get_opcode_spec,   NULL)
[8]2313#endif
[1]2314    PHP_FE(xcache_is_autoglobal,     NULL)
2315    PHP_FE(xcache_inc,               NULL)
2316    PHP_FE(xcache_dec,               NULL)
2317    PHP_FE(xcache_get,               NULL)
2318    PHP_FE(xcache_set,               NULL)
2319    PHP_FE(xcache_isset,             NULL)
2320    PHP_FE(xcache_unset,             NULL)
2321    {NULL, NULL,                     NULL}
2322};
2323/* }}} */
2324
[75]2325/* old signal handlers {{{ */
2326typedef void (*xc_sighandler_t)(int);
2327#define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
2328#include "foreachcoresig.h"
2329#undef FOREACH_SIG
2330/* }}} */
2331static void xcache_signal_handler(int sig);
2332static void xcache_restore_signal_handler() /* {{{ */
[1]2333{
[75]2334#define FOREACH_SIG(sig) do { \
2335    if (old_##sig##_handler != xcache_signal_handler) { \
2336        signal(sig, old_##sig##_handler); \
2337    } \
2338    else { \
2339        signal(sig, SIG_DFL); \
2340    } \
2341} while (0)
2342#include "foreachcoresig.h"
2343#undef FOREACH_SIG
2344}
2345/* }}} */
2346static void xcache_init_signal_handler() /* {{{ */
2347{
2348#define FOREACH_SIG(sig) \
2349    old_##sig##_handler = signal(sig, xcache_signal_handler)
2350#include "foreachcoresig.h"
2351#undef FOREACH_SIG
2352}
2353/* }}} */
2354static void xcache_signal_handler(int sig) /* {{{ */
2355{
2356    xcache_restore_signal_handler();
[1]2357    if (xc_coredump_dir && xc_coredump_dir[0]) {
2358        chdir(xc_coredump_dir);
2359    }
[65]2360    raise(sig);
[1]2361}
2362/* }}} */
2363
2364/* {{{ PHP_INI */
2365
[164]2366static PHP_INI_MH(xc_OnUpdateDummy)
2367{
2368    return SUCCESS;
2369}
2370
2371static PHP_INI_MH(xc_OnUpdateULong)
2372{
2373    zend_ulong *p = (zend_ulong *) mh_arg1;
2374
2375    *p = (zend_ulong) atoi(new_value);
2376    return SUCCESS;
2377}
2378
[1]2379static PHP_INI_MH(xc_OnUpdateBool)
2380{
2381    zend_bool *p = (zend_bool *)mh_arg1;
2382
2383    if (strncasecmp("on", new_value, sizeof("on"))) {
2384        *p = (zend_bool) atoi(new_value);
2385    }
2386    else {
2387        *p = (zend_bool) 1;
2388    }
2389    return SUCCESS;
2390}
2391
2392static PHP_INI_MH(xc_OnUpdateString)
2393{
2394    char **p = (char**)mh_arg1;
2395    if (*p) {
2396        pefree(*p, 1);
2397    }
2398    *p = pemalloc(strlen(new_value) + 1, 1);
2399    strcpy(*p, new_value);
2400    return SUCCESS;
2401}
[82]2402
[114]2403#ifndef ZEND_ENGINE_2
2404#define OnUpdateLong OnUpdateInt
[1]2405#endif
2406
[21]2407#ifdef ZEND_WIN32
2408#   define DEFAULT_PATH "xcache"
2409#else
2410#   define DEFAULT_PATH "/dev/zero"
2411#endif
[1]2412PHP_INI_BEGIN()
[21]2413    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_mmap_path)
[1]2414    PHP_INI_ENTRY1     ("xcache.coredump_directory",      "", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_coredump_dir)
2415    PHP_INI_ENTRY1     ("xcache.test",                   "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_test)
2416    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xc_OnUpdateBool,     &xc_readonly_protection)
[164]2417    /* opcode cache */
2418    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2419    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2420    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2421    PHP_INI_ENTRY1     ("xcache.shm_scheme",          "mmap", PHP_INI_SYSTEM, xc_OnUpdateString,   &xc_shm_scheme)
2422    PHP_INI_ENTRY1     ("xcache.ttl",                    "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_ttl)
2423    PHP_INI_ENTRY1     ("xcache.gc_interval",            "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_php_gc_interval)
2424    /* var cache */
2425    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2426    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2427    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
2428    PHP_INI_ENTRY1     ("xcache.var_maxttl",             "0", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_maxttl)
2429    PHP_INI_ENTRY1     ("xcache.var_gc_interval",      "120", PHP_INI_SYSTEM, xc_OnUpdateULong,    &xc_var_gc_interval)
[1]2430
2431    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,        cacher,            zend_xcache_globals, xcache_globals)
[165]2432    STD_PHP_INI_BOOLEAN("xcache.stat",                   "1", PHP_INI_ALL,    OnUpdateBool,        stat,              zend_xcache_globals, xcache_globals)
[1]2433#ifdef HAVE_XCACHE_OPTIMIZER
2434    STD_PHP_INI_BOOLEAN("xcache.optimizer",              "0", PHP_INI_ALL,    OnUpdateBool,        optimizer,         zend_xcache_globals, xcache_globals)
2435#endif
[254]2436    STD_PHP_INI_ENTRY  ("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong,        var_ttl,           zend_xcache_globals, xcache_globals)
[27]2437#ifdef HAVE_XCACHE_COVERAGER
[204]2438    STD_PHP_INI_BOOLEAN("xcache.coverager"      ,        "0", PHP_INI_ALL,    OnUpdateBool,        coverager,         zend_xcache_globals, xcache_globals)
2439    PHP_INI_ENTRY1     ("xcache.coveragedump_directory",  "", PHP_INI_SYSTEM, xc_OnUpdateDummy,    NULL)
[1]2440#endif
2441PHP_INI_END()
2442/* }}} */
2443/* {{{ PHP_MINFO_FUNCTION(xcache) */
2444static PHP_MINFO_FUNCTION(xcache)
2445{
[82]2446    char buf[100];
2447    char *ptr;
[163]2448    int left, len;
2449    xc_shm_scheme_t *scheme;
[204]2450    char *covdumpdir;
[82]2451
[1]2452    php_info_print_table_start();
[148]2453    php_info_print_table_header(2, "XCache Support", "enabled");
[1]2454    php_info_print_table_row(2, "Version", XCACHE_VERSION);
[26]2455    php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
[1]2456    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "N/A");
[82]2457
2458    if (xc_php_size) {
2459        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
[163]2460        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
[82]2461        php_info_print_table_row(2, "Opcode Cache", buf);
2462        efree(ptr);
2463    }
2464    else {
2465        php_info_print_table_row(2, "Opcode Cache", "disabled");
2466    }
2467    if (xc_var_size) {
2468        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
[163]2469        snprintf(buf, sizeof(buf), "enabled, %s bytes, %d split(s), with %d slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
[82]2470        php_info_print_table_row(2, "Variable Cache", buf);
2471        efree(ptr);
2472    }
2473    else {
2474        php_info_print_table_row(2, "Variable Cache", "disabled");
2475    }
[163]2476
2477    left = sizeof(buf);
2478    ptr = buf;
2479    buf[0] = '\0';
2480    for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
2481        len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
2482        left -= len;
2483        ptr += len;
2484    }
2485    php_info_print_table_row(2, "Shared Memory Schemes", buf);
2486
[26]2487#ifdef HAVE_XCACHE_COVERAGER
[204]2488    if (cfg_get_string("xcache.coveragedump_directory", &covdumpdir) != SUCCESS || !covdumpdir[0]) {
2489        covdumpdir = NULL;
2490    }
2491    php_info_print_table_row(2, "Coverage Auto Dumper", XG(coverager) && covdumpdir ? "enabled" : "disabled");
[26]2492#endif
[1]2493    php_info_print_table_end();
[82]2494
[1]2495    DISPLAY_INI_ENTRIES();
2496}
2497/* }}} */
2498/* {{{ extension startup */
2499static void xc_zend_extension_register(zend_extension *new_extension, DL_HANDLE handle)
2500{
2501    zend_extension extension;
2502
2503    extension = *new_extension;
2504    extension.handle = handle;
2505
2506    zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);
2507
[296]2508    zend_llist_prepend_element(&zend_extensions, &extension);
[305]2509    TRACE("%s", "registered");
[1]2510}
2511
2512static int xc_zend_extension_startup(zend_extension *extension)
2513{
2514    if (extension->startup) {
2515        if (extension->startup(extension) != SUCCESS) {
2516            return FAILURE;
2517        }
2518    }
2519    return SUCCESS;
2520}
2521/* }}} */
[278]2522static int xc_ptr_compare_func(void *p1, void *p2) /* {{{ */
2523{
2524    return p1 == p2;
2525}
2526/* }}} */
2527static int xc_zend_remove_extension(zend_extension *extension) /* {{{ */
2528{
2529    llist_dtor_func_t dtor;
2530
2531    assert(extension);
2532    dtor = zend_extensions.dtor; /* avoid dtor */
2533    zend_extensions.dtor = NULL;
2534    zend_llist_del_element(&zend_extensions, extension, xc_ptr_compare_func);
2535    zend_extensions.dtor = dtor;
[295]2536    return SUCCESS;
[278]2537}
2538/* }}} */
[82]2539static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
2540{
2541    int bits, size;
2542    char *value;
2543
2544    if (cfg_get_string(name, &value) != SUCCESS) {
2545        value = default_value;
2546    }
2547
2548    p->size = zend_atoi(value, strlen(value));
2549    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
2550        /* empty body */
2551    }
2552    p->size = size;
2553    p->bits = bits;
2554    p->mask = size - 1;
2555
2556    return SUCCESS;
2557}
2558/* }}} */
[90]2559static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
[82]2560{
2561    char *value;
2562
2563    if (cfg_get_string(name, &value) != SUCCESS) {
2564        value = default_value;
2565    }
2566
2567    *p = zend_atoi(value, strlen(value));
2568    return SUCCESS;
2569}
2570/* }}} */
[1]2571/* {{{ PHP_MINIT_FUNCTION(xcache) */
2572static PHP_MINIT_FUNCTION(xcache)
2573{
2574    char *env;
[189]2575    zend_extension *ext;
2576    zend_llist_position lpos;
[1]2577
2578    xc_module_gotup = 1;
2579    if (!xc_zend_extension_gotup) {
[296]2580        xc_zend_extension_register(&zend_extension_entry, 0);
2581        xc_zend_extension_startup(&zend_extension_entry);
2582        xc_zend_extension_faked = 1;
[1]2583    }
2584
[190]2585    ext = zend_get_extension("Zend Optimizer");
2586    if (ext) {
2587        /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
2588        ext->op_array_handler = NULL;
2589    }
[189]2590    /* cache if there's an op_array_ctor */
2591    for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
2592            ext;
2593            ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
2594        if (ext->op_array_ctor) {
2595            xc_have_op_array_ctor = 1;
2596            break;
2597        }
2598    }
2599
2600
[92]2601#ifndef PHP_GINIT
[17]2602    ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
[92]2603#endif
[17]2604    REGISTER_INI_ENTRIES();
2605
[1]2606    if (strcmp(sapi_module.name, "cli") == 0) {
2607        if ((env = getenv("XCACHE_TEST")) != NULL) {
2608            zend_alter_ini_entry("xcache.test", sizeof("xcache.test"), env, strlen(env) + 1, PHP_INI_SYSTEM, PHP_INI_STAGE_STARTUP);
2609        }
2610        if (!xc_test) {
2611            /* disable cache for cli except for test */
2612            xc_php_size = xc_var_size = 0;
2613        }
2614    }
2615
[114]2616    xc_config_long(&xc_php_size,       "xcache.size",        "0");
2617    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
2618    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
[82]2619
[164]2620    xc_config_long(&xc_var_size,       "xcache.var_size",    "0");
2621    xc_config_hash(&xc_var_hcache,     "xcache.var_count",   "1");
2622    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",  "8K");
[82]2623
[1]2624    if (xc_php_size <= 0) {
2625        xc_php_size = xc_php_hcache.size = 0;
2626    }
2627    if (xc_var_size <= 0) {
2628        xc_var_size = xc_var_hcache.size = 0;
2629    }
2630
[65]2631    if (xc_coredump_dir && xc_coredump_dir[0]) {
[75]2632        xcache_init_signal_handler();
[65]2633    }
[1]2634
2635    xc_init_constant(module_number TSRMLS_CC);
[148]2636    xc_shm_init_modules();
[1]2637
2638    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
2639        if (!xc_init(module_number TSRMLS_CC)) {
[21]2640            zend_error(E_ERROR, "XCache: Cannot init");
[1]2641            goto err_init;
2642        }
2643        xc_initized = 1;
2644    }
2645
[27]2646#ifdef HAVE_XCACHE_COVERAGER
2647    xc_coverager_init(module_number TSRMLS_CC);
[1]2648#endif
2649
2650    return SUCCESS;
2651
2652err_init:
2653    return FAILURE;
2654}
2655/* }}} */
2656/* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
2657static PHP_MSHUTDOWN_FUNCTION(xcache)
2658{
2659    if (xc_initized) {
2660        xc_destroy();
2661        xc_initized = 0;
2662    }
2663    if (xc_mmap_path) {
2664        pefree(xc_mmap_path, 1);
2665        xc_mmap_path = NULL;
2666    }
[148]2667    if (xc_shm_scheme) {
2668        pefree(xc_shm_scheme, 1);
2669        xc_shm_scheme = NULL;
2670    }
[1]2671
[27]2672#ifdef HAVE_XCACHE_COVERAGER
2673    xc_coverager_destroy();
[1]2674#endif
2675
[65]2676    if (xc_coredump_dir && xc_coredump_dir[0]) {
[75]2677        xcache_restore_signal_handler();
[65]2678    }
[1]2679    if (xc_coredump_dir) {
2680        pefree(xc_coredump_dir, 1);
2681        xc_coredump_dir = NULL;
2682    }
[92]2683#ifndef PHP_GINIT
2684#   ifdef ZTS
[25]2685    ts_free_id(xcache_globals_id);
[92]2686#   else
[1]2687    xc_shutdown_globals(&xcache_globals TSRMLS_CC);
[92]2688#   endif
[1]2689#endif
2690
[278]2691    if (xc_zend_extension_faked) {
2692        zend_extension *ext = zend_get_extension(XCACHE_NAME);
2693        if (ext->shutdown) {
2694            ext->shutdown(ext);
2695        }
2696        xc_zend_remove_extension(ext);
2697    }
[1]2698    UNREGISTER_INI_ENTRIES();
2699    return SUCCESS;
2700}
2701/* }}} */
2702/* {{{ PHP_RINIT_FUNCTION(xcache) */
2703static PHP_RINIT_FUNCTION(xcache)
2704{
2705    xc_request_init(TSRMLS_C);
2706    return SUCCESS;
2707}
2708/* }}} */
2709/* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
2710#ifndef ZEND_ENGINE_2
2711static PHP_RSHUTDOWN_FUNCTION(xcache)
2712#else
2713static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
2714#endif
2715{
[18]2716#ifdef ZEND_ENGINE_2
[11]2717    TSRMLS_FETCH();
[18]2718#endif
[11]2719
[1]2720    xc_request_shutdown(TSRMLS_C);
2721    return SUCCESS;
2722}
2723/* }}} */
2724/* {{{ module definition structure */
2725
2726zend_module_entry xcache_module_entry = {
2727    STANDARD_MODULE_HEADER,
[21]2728    "XCache",
[1]2729    xcache_functions,
2730    PHP_MINIT(xcache),
2731    PHP_MSHUTDOWN(xcache),
2732    PHP_RINIT(xcache),
2733#ifndef ZEND_ENGINE_2
2734    PHP_RSHUTDOWN(xcache),
2735#else
2736    NULL,
2737#endif
2738    PHP_MINFO(xcache),
2739    XCACHE_VERSION,
[92]2740#ifdef PHP_GINIT
2741    PHP_MODULE_GLOBALS(xcache),
2742    PHP_GINIT(xcache),
2743    PHP_GSHUTDOWN(xcache),
2744#endif
[1]2745#ifdef ZEND_ENGINE_2
2746    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
2747#else
2748    NULL,
2749    NULL,
2750#endif
2751    STANDARD_MODULE_PROPERTIES_EX
2752};
2753
2754#ifdef COMPILE_DL_XCACHE
2755ZEND_GET_MODULE(xcache)
2756#endif
2757/* }}} */
[190]2758static startup_func_t xc_last_ext_startup;
2759static zend_llist_element *xc_llist_element;
2760static int xc_zend_startup_last(zend_extension *extension) /* {{{ */
2761{
2762    /* restore */
2763    extension->startup = xc_last_ext_startup;
2764    if (extension->startup) {
2765        if (extension->startup(extension) != SUCCESS) {
2766            return FAILURE;
2767        }
2768    }
2769    xc_zend_extension_register(&zend_extension_entry, 0);
2770    if (!xc_module_gotup) {
2771        return zend_startup_module(&xcache_module_entry);
2772    }
2773    return SUCCESS;
2774}
2775/* }}} */
[1]2776ZEND_DLEXPORT int xcache_zend_startup(zend_extension *extension) /* {{{ */
2777{
2778    xc_zend_extension_gotup = 1;
[190]2779    xc_llist_element = NULL;
2780    if (zend_llist_count(&zend_extensions) > 1) {
2781        zend_llist_position lpos;
2782        zend_extension *ext;
2783
2784        ext = zend_get_extension(XCACHE_NAME);
[278]2785        xc_zend_remove_extension(ext);
[190]2786
2787        ext = (zend_extension *) zend_llist_get_last_ex(&zend_extensions, &lpos);
2788        assert(ext);
2789        xc_last_ext_startup = ext->startup;
2790        ext->startup = xc_zend_startup_last;
2791    }
2792    else if (!xc_module_gotup) {
[1]2793        return zend_startup_module(&xcache_module_entry);
2794    }
2795    return SUCCESS;
2796}
2797/* }}} */
2798ZEND_DLEXPORT void xcache_zend_shutdown(zend_extension *extension) /* {{{ */
2799{
2800    /* empty */
2801}
2802/* }}} */
2803ZEND_DLEXPORT void xcache_statement_handler(zend_op_array *op_array) /* {{{ */
2804{
[27]2805#ifdef HAVE_XCACHE_COVERAGER
2806    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
[1]2807#endif
2808}
2809/* }}} */
2810ZEND_DLEXPORT void xcache_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
2811{
2812#if 0
[27]2813    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
[1]2814#endif
2815}
2816/* }}} */
2817ZEND_DLEXPORT void xcache_fcall_end_handler(zend_op_array *op_array) /* {{{ */
2818{
2819#if 0
[27]2820    xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
[1]2821#endif
2822}
2823/* }}} */
2824/* {{{ zend extension definition structure */
2825ZEND_DLEXPORT zend_extension zend_extension_entry = {
2826    XCACHE_NAME,
2827    XCACHE_VERSION,
2828    XCACHE_AUTHOR,
2829    XCACHE_URL,
2830    XCACHE_COPYRIGHT,
2831    xcache_zend_startup,
2832    xcache_zend_shutdown,
2833    NULL,           /* activate_func_t */
2834    NULL,           /* deactivate_func_t */
2835    NULL,           /* message_handler_func_t */
2836    NULL,           /* op_array_handler_func_t */
2837    xcache_statement_handler,
2838    xcache_fcall_begin_handler,
2839    xcache_fcall_end_handler,
2840    NULL,           /* op_array_ctor_func_t */
2841    NULL,           /* op_array_dtor_func_t */
2842    STANDARD_ZEND_EXTENSION_PROPERTIES
2843};
2844
2845#ifndef ZEND_EXT_API
2846#   define ZEND_EXT_API ZEND_DLEXPORT
2847#endif
2848#if COMPILE_DL_XCACHE
2849ZEND_EXTENSION();
2850#endif
2851/* }}} */
Note: See TracBrowser for help on using the repository browser.