source: trunk/xcache.c @ 669

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

use XCACHE_ERROR_CACHING macro to simplify ifdefs

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