source: branches/1.3/xcache.c @ 623

Last change on this file since 623 was 623, checked in by moo, 5 years ago

merged r514-r539 from trunk

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