source: branches/1.2/xcache.c @ 401

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

merged [400] from trunk->1.2: fix invalid read of free'ed data for hide-and-seek trick. possible fix version string

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