source: svn/trunk/xcache.c @ 311

Last change on this file since 311 was 311, checked in by Xuefer, 10 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.