source: trunk/xcache.c @ 307

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

stack interface changes

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