source: trunk/xcache.c @ 1030

Last change on this file since 1030 was 1030, checked in by moo, 2 years ago

refactor: cleanup initial_compile_file_called

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