source: trunk/xcache.c @ 311

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

restruct cached compile, add md5 table to recognize and merge file with same content

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