source: trunk/xcache.c @ 1029

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

refactor: typo

  • Property svn:eol-style set to native
File size: 114.2 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        return;
1641    }
1642
1643    /* }}} */
1644    /* {{{ prepare */
1645    zend_restore_compiled_filename(h->opened_path ? h->opened_path : (char *) h->filename TSRMLS_CC);
1646
1647#ifdef HAVE_XCACHE_CONSTANT
1648    compiler->new_php.constinfo_cnt  = zend_hash_num_elements(EG(zend_constants)) - old_constinfo_cnt;
1649#endif
1650    compiler->new_php.funcinfo_cnt   = zend_hash_num_elements(CG(function_table)) - old_funcinfo_cnt;
1651    compiler->new_php.classinfo_cnt  = zend_hash_num_elements(CG(class_table))    - old_classinfo_cnt;
1652#ifdef ZEND_ENGINE_2_1
1653    /* {{{ count new_php.autoglobal_cnt */ {
1654        Bucket *b;
1655
1656        compiler->new_php.autoglobal_cnt = 0;
1657        for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
1658            zend_auto_global *auto_global = (zend_auto_global *) b->pData;
1659            /* check if actived */
1660            if (auto_global->auto_global_callback && !auto_global->armed) {
1661                compiler->new_php.autoglobal_cnt ++;
1662            }
1663        }
1664    }
1665    /* }}} */
1666#endif
1667
1668#define X_ALLOC_N(var, cnt) do {     \
1669    if (compiler->new_php.cnt) {                  \
1670        ECALLOC_N(compiler->new_php.var, compiler->new_php.cnt); \
1671        if (!compiler->new_php.var) {             \
1672            goto err_alloc;          \
1673        }                            \
1674    }                                \
1675    else {                           \
1676        compiler->new_php.var = NULL;             \
1677    }                                \
1678} while (0)
1679
1680#ifdef HAVE_XCACHE_CONSTANT
1681    X_ALLOC_N(constinfos,  constinfo_cnt);
1682#endif
1683    X_ALLOC_N(funcinfos,   funcinfo_cnt);
1684    X_ALLOC_N(classinfos,  classinfo_cnt);
1685#ifdef ZEND_ENGINE_2_1
1686    X_ALLOC_N(autoglobals, autoglobal_cnt);
1687#endif
1688#undef X_ALLOC
1689    /* }}} */
1690
1691    /* {{{ shallow copy, pointers only */ {
1692        Bucket *b;
1693        zend_uint i;
1694        zend_uint j;
1695
1696#define COPY_H(vartype, var, cnt, name, datatype) do {        \
1697    for (i = 0, j = 0; b; i ++, b = b->pListNext) {           \
1698        vartype *data = &compiler->new_php.var[j];                         \
1699                                                              \
1700        if (i < old_##cnt) {                                  \
1701            continue;                                         \
1702        }                                                     \
1703        j ++;                                                 \
1704                                                              \
1705        assert(i < old_##cnt + compiler->new_php.cnt);                     \
1706        assert(b->pData);                                     \
1707        memcpy(&data->name, b->pData, sizeof(datatype));      \
1708        UNISW(NOTHING, data->type = b->key.type;)             \
1709        if (UNISW(1, b->key.type == IS_STRING)) {             \
1710            ZSTR_S(data->key)      = BUCKET_KEY_S(b);         \
1711        }                                                     \
1712        else {                                                \
1713            ZSTR_U(data->key)      = BUCKET_KEY_U(b);         \
1714        }                                                     \
1715        data->key_size   = b->nKeyLength;                     \
1716        data->h          = b->h;                              \
1717    }                                                         \
1718} while(0)
1719
1720#ifdef HAVE_XCACHE_CONSTANT
1721        b = EG(zend_constants)->pListHead; COPY_H(xc_constinfo_t, constinfos, constinfo_cnt, constant, zend_constant);
1722#endif
1723        b = CG(function_table)->pListHead; COPY_H(xc_funcinfo_t,  funcinfos,  funcinfo_cnt,  func,     zend_function);
1724        b = CG(class_table)->pListHead;    COPY_H(xc_classinfo_t, classinfos, classinfo_cnt, cest,     xc_cest_t);
1725
1726#undef COPY_H
1727
1728        /* for ZE1, cest need to be fixed inside store */
1729
1730#ifdef ZEND_ENGINE_2_1
1731        /* scan for acatived auto globals */
1732        i = 0;
1733        for (b = CG(auto_globals)->pListHead; b != NULL; b = b->pListNext) {
1734            zend_auto_global *auto_global = (zend_auto_global *) b->pData;
1735            /* check if actived */
1736            if (auto_global->auto_global_callback && !auto_global->armed) {
1737                xc_autoglobal_t *data = &compiler->new_php.autoglobals[i];
1738
1739                assert(i < compiler->new_php.autoglobal_cnt);
1740                i ++;
1741                UNISW(NOTHING, data->type = b->key.type;)
1742                if (UNISW(1, b->key.type == IS_STRING)) {
1743                    ZSTR_S(data->key)     = BUCKET_KEY_S(b);
1744                }
1745                else {
1746                    ZSTR_U(data->key)     = BUCKET_KEY_U(b);
1747                }
1748                data->key_len = b->nKeyLength - 1;
1749                data->h       = b->h;
1750            }
1751        }
1752#endif
1753    }
1754    /* }}} */
1755
1756    /* {{{ collect info for file/dir path */ {
1757        Bucket *b;
1758        xc_const_usage_t const_usage;
1759        unsigned int i;
1760
1761        xc_entry_php_init(&compiler->new_entry, zend_get_compiled_filename(TSRMLS_C) TSRMLS_CC);
1762        memset(&const_usage, 0, sizeof(const_usage));
1763
1764        for (i = 0; i < compiler->new_php.classinfo_cnt; i ++) {
1765            xc_classinfo_t *classinfo = &compiler->new_php.classinfos[i];
1766            zend_class_entry *ce = CestToCePtr(classinfo->cest);
1767            classinfo->methodinfo_cnt = ce->function_table.nTableSize;
1768            if (classinfo->methodinfo_cnt) {
1769                int j;
1770
1771                ECALLOC_N(classinfo->methodinfos, classinfo->methodinfo_cnt);
1772                if (!classinfo->methodinfos) {
1773                    goto err_alloc;
1774                }
1775
1776                for (j = 0, b = ce->function_table.pListHead; b; j ++, b = b->pListNext) {
1777                    xc_collect_op_array_info(compiler, &const_usage, &classinfo->methodinfos[j], (zend_op_array *) b->pData TSRMLS_CC);
1778                }
1779            }
1780            else {
1781                classinfo->methodinfos = NULL;
1782            }
1783        }
1784
1785        for (i = 0; i < compiler->new_php.funcinfo_cnt; i ++) {
1786            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);
1787        }
1788
1789        xc_collect_op_array_info(compiler, &const_usage, &compiler->new_php.op_array_info, compiler->new_php.op_array TSRMLS_CC);
1790
1791        /* file/dir path free unused */
1792#define X_FREE_UNUSED(var) \
1793        if (!const_usage.var##path_used) { \
1794            efree(compiler->new_entry.var##path); \
1795            compiler->new_entry.var##path = NULL; \
1796            compiler->new_entry.var##path_len = 0; \
1797        }
1798        /* filepath is required to restore op_array->filename, so no free filepath here */
1799        X_FREE_UNUSED(dir)
1800#ifdef IS_UNICODE
1801        X_FREE_UNUSED(ufile)
1802        X_FREE_UNUSED(udir)
1803#endif
1804#undef X_FREE_UNUSED
1805    }
1806    /* }}} */
1807#ifdef XCACHE_ERROR_CACHING
1808    compiler->new_php.compilererrors = xc_sandbox_compilererrors(TSRMLS_C);
1809    compiler->new_php.compilererror_cnt = xc_sandbox_compilererror_cnt(TSRMLS_C);
1810#endif
1811#ifndef ZEND_COMPILE_DELAYED_BINDING
1812    /* {{{ find inherited classes that should be early-binding */
1813    compiler->new_php.have_early_binding = 0;
1814    {
1815        zend_uint i;
1816        for (i = 0; i < compiler->new_php.classinfo_cnt; i ++) {
1817            compiler->new_php.classinfos[i].oplineno = -1;
1818        }
1819    }
1820
1821    xc_undo_pass_two(compiler->new_php.op_array TSRMLS_CC);
1822    xc_foreach_early_binding_class(compiler->new_php.op_array, xc_cache_early_binding_class_cb, (void *) &compiler->new_php TSRMLS_CC);
1823    xc_redo_pass_two(compiler->new_php.op_array TSRMLS_CC);
1824    /* }}} */
1825#endif
1826
1827    return;
1828
1829err_alloc:
1830    xc_free_php(&compiler->new_php TSRMLS_CC);
1831
1832err_bailout:
1833err_op_array:
1834
1835    if (catched) {
1836        zend_bailout();
1837    }
1838}
1839/* }}} */
1840static zend_op_array *xc_compile_restore(xc_entry_php_t *stored_entry, xc_entry_data_php_t *stored_php TSRMLS_DC) /* {{{ */
1841{
1842    zend_op_array *op_array;
1843    xc_entry_php_t restored_entry;
1844    xc_entry_data_php_t restored_php;
1845    zend_bool catched;
1846    zend_uint i;
1847
1848    /* still needed because in zend_language_scanner.l, require()/include() check file_handle.handle.stream.handle */
1849    i = 1;
1850    zend_hash_add(&EG(included_files), stored_entry->entry.name.str.val, stored_entry->entry.name.str.len + 1, (void *)&i, sizeof(int), NULL);
1851
1852    CG(in_compilation)    = 1;
1853    CG(compiled_filename) = stored_entry->entry.name.str.val;
1854    CG(zend_lineno)       = 0;
1855    TRACE("restoring %d:%s", stored_entry->file_inode, stored_entry->entry.name.str.val);
1856    xc_processor_restore_xc_entry_php_t(&restored_entry, stored_entry TSRMLS_CC);
1857    xc_processor_restore_xc_entry_data_php_t(stored_entry, &restored_php, stored_php, xc_readonly_protection TSRMLS_CC);
1858    restored_entry.php = &restored_php;
1859#ifdef SHOW_DPRINT
1860    xc_dprint(&restored_entry, 0 TSRMLS_CC);
1861#endif
1862
1863    catched = 0;
1864    zend_try {
1865        op_array = xc_entry_install(&restored_entry TSRMLS_CC);
1866    } zend_catch {
1867        catched = 1;
1868    } zend_end_try();
1869
1870#ifdef HAVE_XCACHE_CONSTANT
1871    if (restored_php.constinfos) {
1872        efree(restored_php.constinfos);
1873    }
1874#endif
1875    if (restored_php.funcinfos) {
1876        efree(restored_php.funcinfos);
1877    }
1878    if (restored_php.classinfos) {
1879        efree(restored_php.classinfos);
1880    }
1881
1882    if (catched) {
1883        zend_bailout();
1884    }
1885    CG(in_compilation)    = 0;
1886    CG(compiled_filename) = NULL;
1887    TRACE("restored %d:%s", stored_entry->file_inode, stored_entry->entry.name.str.val);
1888    return op_array;
1889}
1890/* }}} */
1891static zend_op_array *xc_check_initial_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
1892{
1893    XG(initial_compile_file_called) = 1;
1894    return origin_compile_file(h, type TSRMLS_CC);
1895}
1896/* }}} */
1897typedef struct xc_sandboxed_compiler_t { /* {{{ */
1898    xc_compiler_t *compiler;
1899    /* input */
1900    zend_file_handle *h;
1901    int type;
1902
1903    /* sandbox output */
1904    xc_entry_php_t *stored_entry;
1905    xc_entry_data_php_t *stored_php;
1906} xc_sandboxed_compiler_t; /* {{{ */
1907
1908static zend_op_array *xc_compile_file_sandboxed(void *data TSRMLS_DC) /* {{{ */
1909{
1910    xc_sandboxed_compiler_t *sandboxed_compiler = (xc_sandboxed_compiler_t *) data;
1911    xc_compiler_t *compiler = sandboxed_compiler->compiler;
1912    zend_bool catched = 0;
1913    xc_cache_t *cache = xc_php_caches[compiler->entry_hash.cacheid];
1914    xc_entry_php_t *stored_entry;
1915    xc_entry_data_php_t *stored_php;
1916
1917    /* {{{ compile */
1918    /* make compile inside sandbox */
1919#ifdef HAVE_XCACHE_CONSTANT
1920    compiler->new_php.constinfos  = NULL;
1921#endif
1922    compiler->new_php.funcinfos   = NULL;
1923    compiler->new_php.classinfos  = NULL;
1924#ifdef ZEND_ENGINE_2_1
1925    compiler->new_php.autoglobals = NULL;
1926#endif
1927    memset(&compiler->new_php.op_array_info, 0, sizeof(compiler->new_php.op_array_info));
1928
1929    XG(initial_compile_file_called) = 0;
1930    zend_try {
1931        compiler->new_php.op_array = NULL;
1932        xc_compile_php(compiler, sandboxed_compiler->h, sandboxed_compiler->type TSRMLS_CC);
1933    } zend_catch {
1934        catched = 1;
1935    } zend_end_try();
1936
1937    if (catched
1938     || !compiler->new_php.op_array /* possible ? */
1939     || !XG(initial_compile_file_called)) {
1940        goto err_aftersandbox;
1941    }
1942
1943    /* }}} */
1944#ifdef SHOW_DPRINT
1945    compiler->new_entry.php = &compiler->new_php;
1946    xc_dprint(&compiler->new_entry, 0 TSRMLS_CC);
1947#endif
1948
1949    stored_entry = NULL;
1950    stored_php = NULL;
1951    ENTER_LOCK_EX(cache) { /* {{{ php_store/entry_store */
1952        /* php_store */
1953        stored_php = xc_php_store_unlocked(cache, &compiler->new_php TSRMLS_CC);
1954        if (!stored_php) {
1955            /* error */
1956            break;
1957        }
1958        /* entry_store */
1959        compiler->new_entry.php = stored_php;
1960        stored_entry = xc_entry_php_store_unlocked(cache, compiler->entry_hash.entryslotid, &compiler->new_entry TSRMLS_CC);
1961        if (stored_entry) {
1962            xc_php_addref_unlocked(stored_php);
1963            TRACE(" cached %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
1964            xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
1965        }
1966    } LEAVE_LOCK_EX(cache);
1967    /* }}} */
1968    TRACE("%s", stored_entry ? "stored" : "store failed");
1969
1970    if (catched || !stored_php) {
1971        goto err_aftersandbox;
1972    }
1973
1974    cache->compiling = 0;
1975    xc_free_php(&compiler->new_php TSRMLS_CC);
1976
1977    if (stored_entry) {
1978        sandboxed_compiler->stored_entry = stored_entry;
1979        sandboxed_compiler->stored_php = stored_php;
1980        /* discard newly compiled result, restore from stored one */
1981        if (compiler->new_php.op_array) {
1982#ifdef ZEND_ENGINE_2
1983            destroy_op_array(compiler->new_php.op_array TSRMLS_CC);
1984#else
1985            destroy_op_array(compiler->new_php.op_array);
1986#endif
1987            efree(compiler->new_php.op_array);
1988            compiler->new_php.op_array = NULL;
1989        }
1990        return NULL;
1991    }
1992    else {
1993        return compiler->new_php.op_array;
1994    }
1995
1996err_aftersandbox:
1997    xc_free_php(&compiler->new_php TSRMLS_CC);
1998
1999    cache->compiling = 0;
2000    if (catched) {
2001        cache->errors ++;
2002        zend_bailout();
2003    }
2004    return compiler->new_php.op_array;
2005} /* }}} */
2006static zend_op_array *xc_compile_file_cached(xc_compiler_t *compiler, zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
2007{
2008    /*
2009    if (clog) {
2010        return old;
2011    }
2012
2013    if (cached_entry = getby entry_hash) {
2014        php = cached_entry.php;
2015        php = restore(php);
2016        return php;
2017    }
2018    else {
2019        if (!(php = getby md5)) {
2020            if (clog) {
2021                return old;
2022            }
2023
2024            inside_sandbox {
2025                php = compile;
2026                entry = create entries[entry];
2027            }
2028        }
2029
2030        entry.php = php;
2031        return php;
2032    }
2033    */
2034
2035    xc_entry_php_t *stored_entry;
2036    xc_entry_data_php_t *stored_php;
2037    zend_bool gaveup = 0;
2038    zend_bool catched = 0;
2039    zend_op_array *op_array;
2040    xc_cache_t *cache = xc_php_caches[compiler->entry_hash.cacheid];
2041    xc_sandboxed_compiler_t sandboxed_compiler;
2042
2043    /* stale clogs precheck */
2044    if (XG(request_time) - cache->compiling < 30) {
2045        cache->clogs ++;
2046        return old_compile_file(h, type TSRMLS_CC);
2047    }
2048
2049    /* {{{ entry_lookup/hit/md5_init/php_lookup */
2050    stored_entry = NULL;
2051    stored_php = NULL;
2052
2053    ENTER_LOCK_EX(cache) {
2054        if (!compiler->opened_path && xc_entry_resolve_path_unlocked(compiler, compiler->filename, &stored_entry TSRMLS_CC) == SUCCESS) {
2055            compiler->opened_path = compiler->new_entry.entry.name.str.val;
2056        }
2057        else {
2058            if (!compiler->opened_path && xc_entry_php_resolve_opened_path(compiler, NULL TSRMLS_CC) != SUCCESS) {
2059                gaveup = 1;
2060                break;
2061            }
2062
2063            /* finalize name */
2064            compiler->new_entry.entry.name.str.val = (char *) compiler->opened_path;
2065            compiler->new_entry.entry.name.str.len = strlen(compiler->new_entry.entry.name.str.val);
2066
2067            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);
2068        }
2069
2070        if (stored_entry) {
2071            xc_cache_hit_unlocked(cache TSRMLS_CC);
2072
2073            TRACE(" hit %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
2074            xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
2075            stored_php = stored_entry->php;
2076            break;
2077        }
2078
2079        TRACE("miss entry %d:%s", compiler->new_entry.file_inode, compiler->new_entry.entry.name.str.val);
2080
2081        if (xc_entry_data_php_init_md5(cache, compiler TSRMLS_CC) != SUCCESS) {
2082            gaveup = 1;
2083            break;
2084        }
2085
2086        stored_php = xc_php_find_unlocked(cache, &compiler->new_php TSRMLS_CC);
2087
2088        if (stored_php) {
2089            compiler->new_entry.php = stored_php;
2090            xc_entry_php_init(&compiler->new_entry, compiler->opened_path TSRMLS_CC);
2091            stored_entry = xc_entry_php_store_unlocked(cache, compiler->entry_hash.entryslotid, &compiler->new_entry TSRMLS_CC);
2092            if (stored_entry) {
2093                xc_php_addref_unlocked(stored_php);
2094                TRACE(" cached %d:%s, holding", compiler->new_entry.file_inode, stored_entry->entry.name.str.val);
2095                xc_entry_hold_php_unlocked(cache, stored_entry TSRMLS_CC);
2096            }
2097            else {
2098                gaveup = 1;
2099            }
2100            break;
2101        }
2102
2103        if (XG(request_time) - cache->compiling < 30) {
2104            TRACE("%s", "miss php, but compiling");
2105            cache->clogs ++;
2106            gaveup = 1;
2107            break;
2108        }
2109
2110        TRACE("%s", "miss php, going to compile");
2111        cache->compiling = XG(request_time);
2112    } LEAVE_LOCK_EX(cache);
2113
2114    if (catched) {
2115        cache->compiling = 0;
2116        zend_bailout();
2117    }
2118
2119    /* found entry */
2120    if (stored_entry && stored_php) {
2121        zend_llist_add_element(&CG(open_files), h);
2122        return xc_compile_restore(stored_entry, stored_php TSRMLS_CC);
2123    }
2124
2125    /* gaveup */
2126    if (gaveup) {
2127        return old_compile_file(h, type TSRMLS_CC);
2128    }
2129    /* }}} */
2130
2131    sandboxed_compiler.compiler = compiler;
2132    sandboxed_compiler.h = h;
2133    sandboxed_compiler.type = type;
2134    sandboxed_compiler.stored_php = NULL;
2135    sandboxed_compiler.stored_entry = NULL;
2136    op_array = xc_sandbox(xc_compile_file_sandboxed, (void *) &sandboxed_compiler, h->opened_path ? h->opened_path : h->filename TSRMLS_CC);
2137    if (sandboxed_compiler.stored_entry) {
2138        return xc_compile_restore(sandboxed_compiler.stored_entry, sandboxed_compiler.stored_php TSRMLS_CC);
2139    }
2140    else {
2141        return op_array;
2142    }
2143}
2144/* }}} */
2145static zend_op_array *xc_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
2146{
2147    xc_compiler_t compiler;
2148    zend_op_array *op_array;
2149
2150    assert(xc_initized);
2151
2152    TRACE("xc_compile_file: type=%d name=%s", h->type, h->filename ? h->filename : "NULL");
2153
2154    if (!XG(cacher)
2155     || !h->filename
2156     || !SG(request_info).path_translated
2157     || strstr(h->filename, "://") != NULL
2158#ifdef ZEND_ENGINE_2_3
2159     /* supported by php_resolve_path */
2160     || (!XG(stat) && strstr(PG(include_path), "://") != NULL)
2161#else
2162     || strstr(PG(include_path), "://") != NULL
2163#endif
2164     || !xc_shm || xc_shm->disabled
2165     ) {
2166        TRACE("%s", "cacher not enabled");
2167        return old_compile_file(h, type TSRMLS_CC);
2168    }
2169
2170    /* {{{ entry_init_key */
2171    compiler.opened_path = h->opened_path;
2172    compiler.filename = compiler.opened_path ? compiler.opened_path : h->filename;
2173    compiler.filename_len = strlen(compiler.filename);
2174    if (xc_entry_php_init_key(&compiler TSRMLS_CC) != SUCCESS) {
2175        TRACE("failed to init key for %s", compiler.filename);
2176        return old_compile_file(h, type TSRMLS_CC);
2177    }
2178    /* }}} */
2179
2180    op_array = xc_compile_file_cached(&compiler, h, type TSRMLS_CC);
2181
2182    xc_entry_free_key_php(&compiler.new_entry TSRMLS_CC);
2183
2184    return op_array;
2185}
2186/* }}} */
2187
2188/* gdb helper functions, but N/A for coredump */
2189int xc_is_rw(const void *p) /* {{{ */
2190{
2191    xc_shm_t *shm;
2192    size_t i;
2193
2194    if (xc_php_caches) {
2195        for (i = 0; i < xc_php_hcache.size; i ++) {
2196            shm = xc_php_caches[i]->shm;
2197            if (shm->handlers->is_readwrite(shm, p)) {
2198                return 1;
2199            }
2200        }
2201    }
2202
2203    if (xc_var_caches) {
2204        for (i = 0; i < xc_var_hcache.size; i ++) {
2205            shm = xc_var_caches[i]->shm;
2206            if (shm->handlers->is_readwrite(shm, p)) {
2207                return 1;
2208            }
2209        }
2210    }
2211    return 0;
2212}
2213/* }}} */
2214int xc_is_ro(const void *p) /* {{{ */
2215{
2216    xc_shm_t *shm;
2217    size_t i;
2218
2219    if (xc_php_caches) {
2220        for (i = 0; i < xc_php_hcache.size; i ++) {
2221            shm = xc_php_caches[i]->shm;
2222            if (shm->handlers->is_readonly(shm, p)) {
2223                return 1;
2224            }
2225        }
2226    }
2227
2228    if (xc_var_caches) {
2229        for (i = 0; i < xc_var_hcache.size; i ++) {
2230            shm = xc_var_caches[i]->shm;
2231            if (shm->handlers->is_readonly(shm, p)) {
2232                return 1;
2233            }
2234        }
2235    }
2236    return 0;
2237}
2238/* }}} */
2239int xc_is_shm(const void *p) /* {{{ */
2240{
2241    return xc_is_ro(p) || xc_is_rw(p);
2242}
2243/* }}} */
2244
2245void xc_gc_add_op_array(xc_gc_op_array_t *gc_op_array TSRMLS_DC) /* {{{ */
2246{
2247    zend_llist_add_element(&XG(gc_op_arrays), (void *) gc_op_array);
2248}
2249/* }}} */
2250static void xc_gc_op_array(void *pDest) /* {{{ */
2251{
2252    xc_gc_op_array_t *op_array = (xc_gc_op_array_t *) pDest;
2253    zend_uint i;
2254#ifdef ZEND_ENGINE_2
2255    if (op_array->arg_info) {
2256        for (i = 0; i < op_array->num_args; i++) {
2257            efree((char *) ZSTR_V(op_array->arg_info[i].name));
2258            if (ZSTR_V(op_array->arg_info[i].class_name)) {
2259                efree((char *) ZSTR_V(op_array->arg_info[i].class_name));
2260            }
2261        }
2262        efree(op_array->arg_info);
2263    }
2264#endif
2265    if (op_array->opcodes) {
2266        efree(op_array->opcodes);
2267    }
2268}
2269/* }}} */
2270
2271/* module helper function */
2272static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
2273{
2274    typedef struct {
2275        const char *prefix;
2276        zend_uchar (*getsize)();
2277        const char *(*get)(zend_uchar i);
2278    } xc_meminfo_t;
2279    xc_meminfo_t nameinfos[] = {
2280        { "",        xc_get_op_type_count,   xc_get_op_type   },
2281        { "",        xc_get_data_type_count, xc_get_data_type },
2282        { "",        xc_get_opcode_count,    xc_get_opcode    },
2283        { "OPSPEC_", xc_get_op_spec_count,   xc_get_op_spec   },
2284        { NULL, NULL, NULL }
2285    };
2286    xc_meminfo_t* p;
2287    zend_uchar i, count;
2288    char const_name[96];
2289    int const_name_len;
2290    int undefdone = 0;
2291
2292    for (p = nameinfos; p->getsize; p ++) {
2293        count = p->getsize();
2294        for (i = 0; i < count; i ++) {
2295            const char *name = p->get(i);
2296            if (!name) continue;
2297            if (strcmp(name, "UNDEF") == 0) {
2298                if (undefdone) continue;
2299                undefdone = 1;
2300            }
2301            const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
2302            zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2303        }
2304    }
2305
2306    zend_register_long_constant(ZEND_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2307    zend_register_long_constant(ZEND_STRS("XC_TYPE_PHP"), XC_TYPE_PHP, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2308    zend_register_long_constant(ZEND_STRS("XC_TYPE_VAR"), XC_TYPE_VAR, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2309    zend_register_stringl_constant(ZEND_STRS("XCACHE_VERSION"), ZEND_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2310    zend_register_stringl_constant(ZEND_STRS("XCACHE_MODULES"), ZEND_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
2311    return 0;
2312}
2313/* }}} */
2314static void xc_cache_destroy(xc_cache_t **caches, xc_hash_t *hcache) /* {{{ */
2315{
2316    size_t i;
2317    xc_cache_t *cache;
2318
2319    if (!caches) {
2320        return;
2321    }
2322
2323    for (i = 0; i < hcache->size; i ++) {
2324        cache = caches[i];
2325        if (cache) {
2326            if (cache->lck) {
2327                xc_lock_destroy(cache->lck);
2328            }
2329            /* do NOT free
2330            if (cache->entries) {
2331                cache->mem->handlers->free(cache->mem, cache->entries);
2332            }
2333            cache->mem->handlers->free(cache->mem, cache);
2334            */
2335            cache->shm->handlers->memdestroy(cache->mem);
2336        }
2337    }
2338    free(caches);
2339}
2340/* }}} */
2341static 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) /* {{{ */
2342{
2343    xc_cache_t **caches = NULL, *cache;
2344    xc_mem_t *mem;
2345    time_t now = time(NULL);
2346    size_t i;
2347    xc_memsize_t memsize;
2348
2349    memsize = shmsize / hcache->size;
2350
2351    /* Don't let it break out of mem after ALIGNed
2352     * This is important for
2353     * Simply loop until it fit our need
2354     */
2355    while (ALIGN(memsize) * hcache->size > shmsize && ALIGN(memsize) != memsize) {
2356        if (memsize < ALIGN(1)) {
2357            CHECK(NULL, "cache too small");
2358        }
2359        memsize --;
2360    }
2361
2362    CHECK(caches = calloc(hcache->size, sizeof(xc_cache_t *)), "caches OOM");
2363
2364    for (i = 0; i < hcache->size; i ++) {
2365        CHECK(mem            = shm->handlers->meminit(shm, memsize), "Failed init memory allocator");
2366        CHECK(cache          = mem->handlers->calloc(mem, 1, sizeof(xc_cache_t)), "cache OOM");
2367        CHECK(cache->entries = mem->handlers->calloc(mem, hentry->size, sizeof(xc_entry_t*)), "entries OOM");
2368        if (hphp) {
2369            CHECK(cache->phps= mem->handlers->calloc(mem, hphp->size, sizeof(xc_entry_data_php_t*)), "phps OOM");
2370        }
2371        CHECK(cache->lck     = xc_lock_init(NULL), "can't create lock");
2372
2373        cache->hcache  = hcache;
2374        cache->hentry  = hentry;
2375        cache->hphp    = hphp;
2376        cache->shm     = shm;
2377        cache->mem     = mem;
2378        cache->cacheid = i;
2379        cache->last_gc_deletes = now;
2380        cache->last_gc_expires = now;
2381        caches[i] = cache;
2382    }
2383    return caches;
2384
2385err:
2386    if (caches) {
2387        xc_cache_destroy(caches, hcache);
2388    }
2389    return NULL;
2390}
2391/* }}} */
2392static void xc_destroy() /* {{{ */
2393{
2394    if (old_compile_file) {
2395        zend_compile_file = old_compile_file;
2396        old_compile_file = NULL;
2397    }
2398
2399    if (origin_compile_file) {
2400        zend_compile_file = origin_compile_file;
2401        origin_compile_file = NULL;
2402    }
2403
2404    if (xc_php_caches) {
2405        xc_cache_destroy(xc_php_caches, &xc_php_hcache);
2406        xc_php_caches = NULL;
2407    }
2408
2409    if (xc_var_caches) {
2410        xc_cache_destroy(xc_var_caches, &xc_var_hcache);
2411        xc_var_caches = NULL;
2412    }
2413
2414    if (xc_shm) {
2415        xc_shm_destroy(xc_shm);
2416        xc_shm = NULL;
2417    }
2418
2419    xc_initized = 0;
2420}
2421/* }}} */
2422static int xc_init(int module_number TSRMLS_DC) /* {{{ */
2423{
2424    xc_shmsize_t shmsize = ALIGN(xc_php_size) + ALIGN(xc_var_size);
2425
2426    xc_php_caches = xc_var_caches = NULL;
2427    xc_shm = NULL;
2428
2429    if (shmsize < (size_t) xc_php_size || shmsize < (size_t) xc_var_size) {
2430        zend_error(E_ERROR, "XCache: neither xcache.size nor xcache.var_size can be negative");
2431        goto err;
2432    }
2433
2434    if (xc_php_size || xc_var_size) {
2435        CHECK(xc_shm = xc_shm_init(xc_shm_scheme, shmsize, xc_readonly_protection, xc_mmap_path, NULL), "Cannot create shm");
2436        if (!xc_shm->handlers->can_readonly(xc_shm)) {
2437            xc_readonly_protection = 0;
2438        }
2439
2440        if (xc_php_size) {
2441            old_compile_file = zend_compile_file;
2442            zend_compile_file = xc_compile_file;
2443
2444            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");
2445        }
2446
2447        if (xc_var_size) {
2448            CHECK(xc_var_caches = xc_cache_init(xc_shm, &xc_var_hcache, &xc_var_hentry, NULL, xc_var_size), "failed init variable cache");
2449        }
2450    }
2451    return SUCCESS;
2452
2453err:
2454    if (xc_php_caches || xc_var_caches) {
2455        xc_destroy();
2456        /* shm destroied in xc_destroy() */
2457    }
2458    else if (xc_shm) {
2459        xc_destroy();
2460        xc_shm_destroy(xc_shm);
2461        xc_shm = NULL;
2462    }
2463    return 0;
2464}
2465/* }}} */
2466static void xc_request_init(TSRMLS_D) /* {{{ */
2467{
2468    size_t i;
2469
2470    if (!XG(internal_table_copied)) {
2471        zend_function tmp_func;
2472        xc_cest_t tmp_cest;
2473
2474#ifdef HAVE_XCACHE_CONSTANT
2475        zend_hash_destroy(&XG(internal_constant_table));
2476#endif
2477        zend_hash_destroy(&XG(internal_function_table));
2478        zend_hash_destroy(&XG(internal_class_table));
2479
2480#ifdef HAVE_XCACHE_CONSTANT
2481        zend_hash_init_ex(&XG(internal_constant_table), 20,  NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
2482#endif
2483        zend_hash_init_ex(&XG(internal_function_table), 100, NULL, NULL, 1, 0);
2484        zend_hash_init_ex(&XG(internal_class_table),    10,  NULL, NULL, 1, 0);
2485
2486#ifdef HAVE_XCACHE_CONSTANT
2487        xc_copy_internal_zend_constants(&XG(internal_constant_table), EG(zend_constants));
2488#endif
2489        zend_hash_copy(&XG(internal_function_table), CG(function_table), NULL, &tmp_func, sizeof(tmp_func));
2490        zend_hash_copy(&XG(internal_class_table), CG(class_table), NULL, &tmp_cest, sizeof(tmp_cest));
2491
2492        XG(internal_table_copied) = 1;
2493    }
2494    if (xc_php_caches && !XG(php_holds)) {
2495        XG(php_holds) = calloc(xc_php_hcache.size, sizeof(xc_stack_t));
2496        for (i = 0; i < xc_php_hcache.size; i ++) {
2497            xc_stack_init(&XG(php_holds[i]));
2498        }
2499    }
2500
2501    if (xc_var_caches && !XG(var_holds)) {
2502        XG(var_holds) = calloc(xc_var_hcache.size, sizeof(xc_stack_t));
2503        for (i = 0; i < xc_var_hcache.size; i ++) {
2504            xc_stack_init(&XG(var_holds[i]));
2505        }
2506    }
2507
2508#ifdef ZEND_ENGINE_2
2509    zend_llist_init(&XG(gc_op_arrays), sizeof(xc_gc_op_array_t), xc_gc_op_array, 0);
2510#endif
2511
2512#if PHP_API_VERSION <= 20041225
2513    XG(request_time) = time(NULL);
2514#else
2515    XG(request_time) = sapi_get_request_time(TSRMLS_C);
2516#endif
2517}
2518/* }}} */
2519static void xc_request_shutdown(TSRMLS_D) /* {{{ */
2520{
2521    if (xc_shm && !xc_shm->disabled) {
2522        xc_entry_unholds(TSRMLS_C);
2523        xc_gc_expires_php(TSRMLS_C);
2524        xc_gc_expires_var(TSRMLS_C);
2525        xc_gc_deletes(TSRMLS_C);
2526    }
2527#ifdef ZEND_ENGINE_2
2528    zend_llist_destroy(&XG(gc_op_arrays));
2529#endif
2530}
2531/* }}} */
2532/* {{{ PHP_GINIT_FUNCTION(xcache) */
2533static
2534#ifdef PHP_GINIT_FUNCTION
2535PHP_GINIT_FUNCTION(xcache)
2536#else
2537void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
2538#endif
2539{
2540    memset(xcache_globals, 0, sizeof(zend_xcache_globals));
2541
2542#ifdef HAVE_XCACHE_CONSTANT
2543    zend_hash_init_ex(&xcache_globals->internal_constant_table, 1, NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
2544#endif
2545    zend_hash_init_ex(&xcache_globals->internal_function_table, 1, NULL, NULL, 1, 0);
2546    zend_hash_init_ex(&xcache_globals->internal_class_table,    1, NULL, NULL, 1, 0);
2547}
2548/* }}} */
2549/* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
2550static
2551#ifdef PHP_GSHUTDOWN_FUNCTION
2552PHP_GSHUTDOWN_FUNCTION(xcache)
2553#else
2554void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
2555#endif
2556{
2557    size_t i;
2558
2559    if (xcache_globals->php_holds != NULL) {
2560        for (i = 0; i < xc_php_hcache.size; i ++) {
2561            xc_stack_destroy(&xcache_globals->php_holds[i]);
2562        }
2563        free(xcache_globals->php_holds);
2564        xcache_globals->php_holds = NULL;
2565    }
2566
2567    if (xcache_globals->var_holds != NULL) {
2568        for (i = 0; i < xc_var_hcache.size; i ++) {
2569            xc_stack_destroy(&xcache_globals->var_holds[i]);
2570        }
2571        free(xcache_globals->var_holds);
2572        xcache_globals->var_holds = NULL;
2573    }
2574
2575    if (xcache_globals->internal_table_copied) {
2576#ifdef HAVE_XCACHE_CONSTANT
2577        zend_hash_destroy(&xcache_globals->internal_constant_table);
2578#endif
2579        zend_hash_destroy(&xcache_globals->internal_function_table);
2580        zend_hash_destroy(&xcache_globals->internal_class_table);
2581    }
2582}
2583/* }}} */
2584
2585/* user functions */
2586static int xcache_admin_auth_check(TSRMLS_D) /* {{{ */
2587{
2588    zval **server = NULL;
2589    zval **user = NULL;
2590    zval **pass = NULL;
2591    char *admin_user = NULL;
2592    char *admin_pass = NULL;
2593    HashTable *ht;
2594
2595    /* auth disabled, nothing to do.. */
2596    if (!XG(auth_enabled)) {
2597        return 1;
2598    }
2599
2600    if (cfg_get_string("xcache.admin.user", &admin_user) == FAILURE || !admin_user[0]) {
2601        admin_user = NULL;
2602    }
2603    if (cfg_get_string("xcache.admin.pass", &admin_pass) == FAILURE || !admin_pass[0]) {
2604        admin_pass = NULL;
2605    }
2606
2607    if (admin_user == NULL || admin_pass == NULL) {
2608        php_error_docref(XCACHE_WIKI_URL "/InstallAdministration" TSRMLS_CC, E_ERROR,
2609                "xcache.admin.user and/or xcache.admin.pass settings is not configured."
2610                " Make sure you've modified the correct php ini file for your php used in webserver.");
2611        zend_bailout();
2612    }
2613    if (strlen(admin_pass) != 32) {
2614        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));
2615        zend_bailout();
2616    }
2617
2618#ifdef ZEND_ENGINE_2_1
2619    zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC);
2620#endif
2621    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != SUCCESS || Z_TYPE_PP(server) != IS_ARRAY) {
2622        php_error_docref(NULL TSRMLS_CC, E_ERROR, "_SERVER is corrupted");
2623        zend_bailout();
2624    }
2625    ht = HASH_OF((*server));
2626
2627    if (zend_hash_find(ht, "PHP_AUTH_USER", sizeof("PHP_AUTH_USER"), (void **) &user) == FAILURE) {
2628        user = NULL;
2629    }
2630    else if (Z_TYPE_PP(user) != IS_STRING) {
2631        user = NULL;
2632    }
2633
2634    if (zend_hash_find(ht, "PHP_AUTH_PW", sizeof("PHP_AUTH_PW"), (void **) &pass) == FAILURE) {
2635        pass = NULL;
2636    }
2637    else if (Z_TYPE_PP(pass) != IS_STRING) {
2638        pass = NULL;
2639    }
2640
2641    if (user != NULL && pass != NULL && strcmp(admin_user, Z_STRVAL_PP(user)) == 0) {
2642        PHP_MD5_CTX context;
2643        char md5str[33];
2644        unsigned char digest[16];
2645
2646        PHP_MD5Init(&context);
2647        PHP_MD5Update(&context, (unsigned char *) Z_STRVAL_PP(pass), Z_STRLEN_PP(pass));
2648        PHP_MD5Final(digest, &context);
2649
2650        md5str[0] = '\0';
2651        make_digest(md5str, digest);
2652        if (strcmp(admin_pass, md5str) == 0) {
2653            return 1;
2654        }
2655    }
2656
2657#define STR "HTTP/1.0 401 Unauthorized"
2658    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2659#undef STR
2660#define STR "WWW-authenticate: Basic Realm=\"XCache Administration\""
2661    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2662#undef STR
2663#define STR "Content-type: text/html; charset=UTF-8"
2664    sapi_add_header_ex(STR, sizeof(STR) - 1, 1, 1 TSRMLS_CC);
2665#undef STR
2666    ZEND_PUTS("<html>\n");
2667    ZEND_PUTS("<head><title>XCache Authentication Failed</title></head>\n");
2668    ZEND_PUTS("<body>\n");
2669    ZEND_PUTS("<h1>XCache Authentication Failed</h1>\n");
2670    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");
2671    ZEND_PUTS("<ul>\n");
2672    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");
2673    ZEND_PUTS("<li>Make sure the md5 password is generated correctly. You may use <a href=\"mkpassword.php\">mkpassword.php</a></li>\n");
2674    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");
2675    ZEND_PUTS("</ul>\n");
2676    ZEND_PUTS("Check <a href=\"" XCACHE_WIKI_URL "/InstallAdministration\">XCache wiki page</a> for more information.\n");
2677    ZEND_PUTS("</body>\n");
2678    ZEND_PUTS("</html>\n");
2679
2680    zend_bailout();
2681    return 0;
2682}
2683/* }}} */
2684static void xc_clear(long type, xc_cache_t *cache TSRMLS_DC) /* {{{ */
2685{
2686    xc_entry_t *e, *next;
2687    int entryslotid, c;
2688
2689    ENTER_LOCK(cache) {
2690        for (entryslotid = 0, c = cache->hentry->size; entryslotid < c; entryslotid ++) {
2691            for (e = cache->entries[entryslotid]; e; e = next) {
2692                next = e->next;
2693                xc_entry_remove_unlocked(type, cache, entryslotid, e TSRMLS_CC);
2694            }
2695            cache->entries[entryslotid] = NULL;
2696        }
2697    } LEAVE_LOCK(cache);
2698} /* }}} */
2699/* {{{ xcache_admin_operate */
2700typedef enum { XC_OP_COUNT, XC_OP_INFO, XC_OP_LIST, XC_OP_CLEAR } xcache_op_type;
2701static void xcache_admin_operate(xcache_op_type optype, INTERNAL_FUNCTION_PARAMETERS)
2702{
2703    long type;
2704    int size;
2705    xc_cache_t **caches, *cache;
2706    long id = 0;
2707
2708    xcache_admin_auth_check(TSRMLS_C);
2709
2710    if (!xc_initized || !xc_shm || xc_shm->disabled) {
2711        RETURN_NULL();
2712    }
2713
2714    switch (optype) {
2715        case XC_OP_COUNT:
2716            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type) == FAILURE) {
2717                return;
2718            }
2719            break;
2720        case XC_OP_CLEAR:
2721            id = -1;
2722            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &type, &id) == FAILURE) {
2723                return;
2724            }
2725            break;
2726        default:
2727            if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &type, &id) == FAILURE) {
2728                return;
2729            }
2730    }
2731
2732    switch (type) {
2733        case XC_TYPE_PHP:
2734            size = xc_php_hcache.size;
2735            caches = xc_php_caches;
2736            break;
2737
2738        case XC_TYPE_VAR:
2739            size = xc_var_hcache.size;
2740            caches = xc_var_caches;
2741            break;
2742
2743        default:
2744            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown type %ld", type);
2745            RETURN_FALSE;
2746    }
2747
2748    switch (optype) {
2749        case XC_OP_COUNT:
2750            RETURN_LONG(caches ? size : 0)
2751            break;
2752
2753        case XC_OP_INFO:
2754        case XC_OP_LIST:
2755            if (!caches || id < 0 || id >= size) {
2756                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2757                RETURN_FALSE;
2758            }
2759
2760            array_init(return_value);
2761
2762            cache = caches[id];
2763            ENTER_LOCK(cache) {
2764                if (optype == XC_OP_INFO) {
2765                    xc_fillinfo_unlocked(type, cache, return_value TSRMLS_CC);
2766                }
2767                else {
2768                    xc_filllist_unlocked(type, cache, return_value TSRMLS_CC);
2769                }
2770            } LEAVE_LOCK(cache);
2771            break;
2772
2773        case XC_OP_CLEAR:
2774            if (!caches || id < -1 || id >= size) {
2775                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cache not exists");
2776                RETURN_FALSE;
2777            }
2778
2779            if (id == -1) {
2780                for (id = 0; id < size; ++id) {
2781                    xc_clear(type, caches[id] TSRMLS_CC);
2782                }
2783            }
2784            else {
2785                xc_clear(type, caches[id] TSRMLS_CC);
2786            }
2787
2788            xc_gc_deletes(TSRMLS_C);
2789            break;
2790
2791        default:
2792            assert(0);
2793    }
2794}
2795/* }}} */
2796/* {{{ proto int xcache_count(int type)
2797   Return count of cache on specified cache type */
2798PHP_FUNCTION(xcache_count)
2799{
2800    xcache_admin_operate(XC_OP_COUNT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2801}
2802/* }}} */
2803/* {{{ proto array xcache_info(int type, int id)
2804   Get cache info by id on specified cache type */
2805PHP_FUNCTION(xcache_info)
2806{
2807    xcache_admin_operate(XC_OP_INFO, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2808}
2809/* }}} */
2810/* {{{ proto array xcache_list(int type, int id)
2811   Get cache entries list by id on specified cache type */
2812PHP_FUNCTION(xcache_list)
2813{
2814    xcache_admin_operate(XC_OP_LIST, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2815}
2816/* }}} */
2817/* {{{ proto array xcache_clear_cache(int type, [ int id = -1 ])
2818   Clear cache by id on specified cache type */
2819PHP_FUNCTION(xcache_clear_cache)
2820{
2821    xcache_admin_operate(XC_OP_CLEAR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
2822}
2823/* }}} */
2824
2825#define VAR_DISABLED_WARNING() do { \
2826        php_error_docref(NULL TSRMLS_CC, E_WARNING, "XCache var cache was not initialized properly. Check php log for actual reason"); \
2827} while (0)
2828
2829static int xc_entry_var_init_key(xc_entry_var_t *entry_var, xc_entry_hash_t *entry_hash, zval *name TSRMLS_DC) /* {{{ */
2830{
2831    xc_hash_value_t hv;
2832
2833    switch (name->type) {
2834#ifdef IS_UNICODE
2835        case IS_UNICODE:
2836        case IS_STRING:
2837#endif
2838        default:
2839#ifdef IS_UNICODE
2840            convert_to_unicode(name);
2841#else
2842            convert_to_string(name);
2843#endif
2844    }
2845
2846#ifdef IS_UNICODE
2847    entry_var->name_type = name->type;
2848#endif
2849    entry_var->entry.name = name->value;
2850
2851    hv = xc_entry_hash_var((xc_entry_t *) entry_var TSRMLS_CC);
2852
2853    entry_hash->cacheid = (hv & xc_var_hcache.mask);
2854    hv >>= xc_var_hcache.bits;
2855    entry_hash->entryslotid = (hv & xc_var_hentry.mask);
2856    return SUCCESS;
2857}
2858/* }}} */
2859/* {{{ proto mixed xcache_get(string name)
2860   Get cached data by specified name */
2861PHP_FUNCTION(xcache_get)
2862{
2863    xc_entry_hash_t entry_hash;
2864    xc_cache_t *cache;
2865    xc_entry_var_t entry_var, *stored_entry_var;
2866    zval *name;
2867
2868    if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
2869        VAR_DISABLED_WARNING();
2870        RETURN_NULL();
2871    }
2872
2873    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2874        return;
2875    }
2876    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2877    cache = xc_var_caches[entry_hash.cacheid];
2878
2879    ENTER_LOCK(cache) {
2880        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);
2881        if (stored_entry_var) {
2882            /* return */
2883            xc_processor_restore_zval(return_value, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
2884            xc_cache_hit_unlocked(cache TSRMLS_CC);
2885        }
2886        else {
2887            RETVAL_NULL();
2888        }
2889    } LEAVE_LOCK(cache);
2890}
2891/* }}} */
2892/* {{{ proto bool  xcache_set(string name, mixed value [, int ttl])
2893   Store data to cache by specified name */
2894PHP_FUNCTION(xcache_set)
2895{
2896    xc_entry_hash_t entry_hash;
2897    xc_cache_t *cache;
2898    xc_entry_var_t entry_var, *stored_entry_var;
2899    zval *name;
2900    zval *value;
2901
2902    if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
2903        VAR_DISABLED_WARNING();
2904        RETURN_NULL();
2905    }
2906
2907    entry_var.entry.ttl = XG(var_ttl);
2908    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|l", &name, &value, &entry_var.entry.ttl) == FAILURE) {
2909        return;
2910    }
2911
2912    if (Z_TYPE_P(value) == IS_OBJECT) {
2913        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Objects cannot be stored in the variable cache. Use serialize before xcache_set");
2914        RETURN_NULL();
2915    }
2916
2917    /* max ttl */
2918    if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
2919        entry_var.entry.ttl = xc_var_maxttl;
2920    }
2921
2922    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2923    cache = xc_var_caches[entry_hash.cacheid];
2924
2925    ENTER_LOCK(cache) {
2926        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);
2927        if (stored_entry_var) {
2928            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
2929        }
2930        entry_var.value = value;
2931        RETVAL_BOOL(xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC) != NULL ? 1 : 0);
2932    } LEAVE_LOCK(cache);
2933}
2934/* }}} */
2935/* {{{ proto bool  xcache_isset(string name)
2936   Check if an entry exists in cache by specified name */
2937PHP_FUNCTION(xcache_isset)
2938{
2939    xc_entry_hash_t entry_hash;
2940    xc_cache_t *cache;
2941    xc_entry_var_t entry_var, *stored_entry_var;
2942    zval *name;
2943
2944    if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
2945        VAR_DISABLED_WARNING();
2946        RETURN_FALSE;
2947    }
2948
2949    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2950        return;
2951    }
2952    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2953    cache = xc_var_caches[entry_hash.cacheid];
2954
2955    ENTER_LOCK(cache) {
2956        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);
2957        if (stored_entry_var) {
2958            xc_cache_hit_unlocked(cache TSRMLS_CC);
2959            RETVAL_TRUE;
2960            /* return */
2961        }
2962        else {
2963            RETVAL_FALSE;
2964        }
2965
2966    } LEAVE_LOCK(cache);
2967}
2968/* }}} */
2969/* {{{ proto bool  xcache_unset(string name)
2970   Unset existing data in cache by specified name */
2971PHP_FUNCTION(xcache_unset)
2972{
2973    xc_entry_hash_t entry_hash;
2974    xc_cache_t *cache;
2975    xc_entry_var_t entry_var, *stored_entry_var;
2976    zval *name;
2977
2978    if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
2979        VAR_DISABLED_WARNING();
2980        RETURN_FALSE;
2981    }
2982
2983    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
2984        return;
2985    }
2986    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
2987    cache = xc_var_caches[entry_hash.cacheid];
2988
2989    ENTER_LOCK(cache) {
2990        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);
2991        if (stored_entry_var) {
2992            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
2993            RETVAL_TRUE;
2994        }
2995        else {
2996            RETVAL_FALSE;
2997        }
2998    } LEAVE_LOCK(cache);
2999}
3000/* }}} */
3001/* {{{ proto bool  xcache_unset_by_prefix(string prefix)
3002   Unset existing data in cache by specified prefix */
3003PHP_FUNCTION(xcache_unset_by_prefix)
3004{
3005    zval *prefix;
3006    int i, iend;
3007
3008    if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
3009        VAR_DISABLED_WARNING();
3010        RETURN_FALSE;
3011    }
3012
3013    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &prefix) == FAILURE) {
3014        return;
3015    }
3016
3017    for (i = 0, iend = xc_var_hcache.size; i < iend; i ++) {
3018        xc_cache_t *cache = xc_var_caches[i];
3019        ENTER_LOCK(cache) {
3020            int entryslotid, jend;
3021            for (entryslotid = 0, jend = cache->hentry->size; entryslotid < jend; entryslotid ++) {
3022                xc_entry_t *entry, *next;
3023                for (entry = cache->entries[entryslotid]; entry; entry = next) {
3024                    next = entry->next;
3025                    if (xc_entry_has_prefix_unlocked(XC_TYPE_VAR, entry, prefix)) {
3026                        xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entryslotid, entry TSRMLS_CC);
3027                    }
3028                }
3029            }
3030        } LEAVE_LOCK(cache);
3031    }
3032}
3033/* }}} */
3034static inline void xc_var_inc_dec(int inc, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
3035{
3036    xc_entry_hash_t entry_hash;
3037    xc_cache_t *cache;
3038    xc_entry_var_t entry_var, *stored_entry_var;
3039    zval *name;
3040    long count = 1;
3041    long value = 0;
3042    zval oldzval;
3043
3044    if (!xc_var_caches || !xc_shm || xc_shm->disabled) {
3045        VAR_DISABLED_WARNING();
3046        RETURN_NULL();
3047    }
3048
3049    entry_var.entry.ttl = XG(var_ttl);
3050    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &name, &count, &entry_var.entry.ttl) == FAILURE) {
3051        return;
3052    }
3053
3054    /* max ttl */
3055    if (xc_var_maxttl && (!entry_var.entry.ttl || entry_var.entry.ttl > xc_var_maxttl)) {
3056        entry_var.entry.ttl = xc_var_maxttl;
3057    }
3058
3059    xc_entry_var_init_key(&entry_var, &entry_hash, name TSRMLS_CC);
3060    cache = xc_var_caches[entry_hash.cacheid];
3061
3062    ENTER_LOCK(cache) {
3063        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);
3064        if (stored_entry_var) {
3065            TRACE("incdec: got entry_var %s", entry_var.entry.name.str.val);
3066            /* do it in place */
3067            if (Z_TYPE_P(stored_entry_var->value) == IS_LONG) {
3068                zval *zv;
3069                stored_entry_var->entry.ctime = XG(request_time);
3070                stored_entry_var->entry.ttl   = entry_var.entry.ttl;
3071                TRACE("%s", "incdec: islong");
3072                value = Z_LVAL_P(stored_entry_var->value);
3073                value += (inc == 1 ? count : - count);
3074                RETVAL_LONG(value);
3075
3076                zv = (zval *) cache->shm->handlers->to_readwrite(cache->shm, (char *) stored_entry_var->value);
3077                Z_LVAL_P(zv) = value;
3078                ++cache->updates;
3079                break; /* leave lock */
3080            }
3081
3082            TRACE("%s", "incdec: notlong");
3083            xc_processor_restore_zval(&oldzval, stored_entry_var->value, stored_entry_var->have_references TSRMLS_CC);
3084            convert_to_long(&oldzval);
3085            value = Z_LVAL(oldzval);
3086            zval_dtor(&oldzval);
3087        }
3088        else {
3089            TRACE("incdec: %s not found", entry_var.entry.name.str.val);
3090        }
3091
3092        value += (inc == 1 ? count : - count);
3093        RETVAL_LONG(value);
3094        entry_var.value = return_value;
3095
3096        if (stored_entry_var) {
3097            entry_var.entry.atime = stored_entry_var->entry.atime;
3098            entry_var.entry.ctime = stored_entry_var->entry.ctime;
3099            entry_var.entry.hits  = stored_entry_var->entry.hits;
3100            xc_entry_remove_unlocked(XC_TYPE_VAR, cache, entry_hash.entryslotid, (xc_entry_t *) stored_entry_var TSRMLS_CC);
3101        }
3102        xc_entry_var_store_unlocked(cache, entry_hash.entryslotid, &entry_var TSRMLS_CC);
3103    } LEAVE_LOCK(cache);
3104}
3105/* }}} */
3106/* {{{ proto int xcache_inc(string name [, int value [, int ttl]])
3107   Increase an int counter in cache by specified name, create it if not exists */
3108PHP_FUNCTION(xcache_inc)
3109{
3110    xc_var_inc_dec(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
3111}
3112/* }}} */
3113/* {{{ proto int xcache_dec(string name [, int value [, int ttl]])
3114   Decrease an int counter in cache by specified name, create it if not exists */
3115PHP_FUNCTION(xcache_dec)
3116{
3117    xc_var_inc_dec(-1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
3118}
3119/* }}} */
3120/* {{{ proto int xcache_get_refcount(mixed variable)
3121   XCache internal uses only: Get reference count of variable */
3122PHP_FUNCTION(xcache_get_refcount)
3123{
3124    zval *variable;
3125    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
3126        RETURN_NULL();
3127    }
3128
3129    RETURN_LONG(Z_REFCOUNT(*variable));
3130}
3131/* }}} */
3132/* {{{ proto bool xcache_get_isref(mixed variable)
3133   XCache internal uses only: Check if variable data is marked referenced */
3134#ifdef ZEND_BEGIN_ARG_INFO_EX
3135ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_isref, 0, 0, 1)
3136    ZEND_ARG_INFO(1, variable)
3137ZEND_END_ARG_INFO()
3138#else
3139static unsigned char arginfo_xcache_get_isref[] = { 1, BYREF_FORCE };
3140#endif
3141
3142PHP_FUNCTION(xcache_get_isref)
3143{
3144    zval *variable;
3145    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
3146        RETURN_NULL();
3147    }
3148
3149    RETURN_BOOL(Z_ISREF(*variable) && Z_REFCOUNT(*variable) >= 3);
3150}
3151/* }}} */
3152#ifdef HAVE_XCACHE_DPRINT
3153/* {{{ proto bool  xcache_dprint(mixed value)
3154   Prints variable (or value) internal struct (debug only) */
3155PHP_FUNCTION(xcache_dprint)
3156{
3157    zval *value;
3158
3159    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
3160        return;
3161    }
3162    xc_dprint_zval(value, 0 TSRMLS_CC);
3163}
3164/* }}} */
3165#endif
3166/* {{{ proto string xcache_asm(string filename)
3167 */
3168#ifdef HAVE_XCACHE_ASSEMBLER
3169PHP_FUNCTION(xcache_asm)
3170{
3171}
3172#endif
3173/* }}} */
3174/* {{{ proto string xcache_encode(string filename)
3175   Encode php file into XCache opcode encoded format */
3176#ifdef HAVE_XCACHE_ENCODER
3177PHP_FUNCTION(xcache_encode)
3178{
3179}
3180#endif
3181/* }}} */
3182/* {{{ proto bool xcache_decode_file(string filename)
3183   Decode(load) opcode from XCache encoded format file */
3184#ifdef HAVE_XCACHE_DECODER
3185PHP_FUNCTION(xcache_decode_file)
3186{
3187}
3188#endif
3189/* }}} */
3190/* {{{ proto bool xcache_decode_string(string data)
3191   Decode(load) opcode from XCache encoded format data */
3192#ifdef HAVE_XCACHE_DECODER
3193PHP_FUNCTION(xcache_decode_string)
3194{
3195}
3196#endif
3197/* }}} */
3198/* {{{ xc_call_getter */
3199typedef const char *(xc_name_getter_t)(zend_uchar type);
3200static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
3201{
3202    long spec;
3203    const char *name;
3204
3205    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
3206        return;
3207    }
3208    if (spec >= 0 && spec < count) {
3209        name = getter((zend_uchar) spec);
3210        if (name) {
3211            /* RETURN_STRING */
3212            int len = strlen(name);
3213            return_value->value.str.len = len;
3214            return_value->value.str.val = estrndup(name, len);
3215            return_value->type = IS_STRING; 
3216            return;
3217        }
3218    }
3219    RETURN_NULL();
3220}
3221/* }}} */
3222/* {{{ proto string xcache_get_op_type(int op_type) */
3223PHP_FUNCTION(xcache_get_op_type)
3224{
3225    xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3226}
3227/* }}} */
3228/* {{{ proto string xcache_get_data_type(int type) */
3229PHP_FUNCTION(xcache_get_data_type)
3230{
3231    xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3232}
3233/* }}} */
3234/* {{{ proto string xcache_get_opcode(int opcode) */
3235PHP_FUNCTION(xcache_get_opcode)
3236{
3237    xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3238}
3239/* }}} */
3240/* {{{ proto string xcache_get_op_spec(int op_type) */
3241PHP_FUNCTION(xcache_get_op_spec)
3242{
3243    xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
3244}
3245/* }}} */
3246/* {{{ proto string xcache_get_opcode_spec(int opcode) */
3247PHP_FUNCTION(xcache_get_opcode_spec)
3248{
3249    long spec;
3250    const xc_opcode_spec_t *opspec;
3251
3252    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
3253        return;
3254    }
3255    if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
3256        opspec = xc_get_opcode_spec((zend_uchar) spec);
3257        if (opspec) {
3258            array_init(return_value);
3259            add_assoc_long_ex(return_value, ZEND_STRS("ext"), opspec->ext);
3260            add_assoc_long_ex(return_value, ZEND_STRS("op1"), opspec->op1);
3261            add_assoc_long_ex(return_value, ZEND_STRS("op2"), opspec->op2);
3262            add_assoc_long_ex(return_value, ZEND_STRS("res"), opspec->res);
3263            return;
3264        }
3265    }
3266    RETURN_NULL();
3267}
3268/* }}} */
3269/* {{{ proto mixed xcache_get_special_value(zval value)
3270   XCache internal use only: For decompiler to get static value with type fixed */
3271PHP_FUNCTION(xcache_get_special_value)
3272{
3273    zval *value;
3274
3275    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
3276        return;
3277    }
3278
3279    switch ((Z_TYPE_P(value) & IS_CONSTANT_TYPE_MASK)) {
3280    case IS_CONSTANT:
3281        *return_value = *value;
3282        zval_copy_ctor(return_value);
3283        return_value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
3284        break;
3285
3286    case IS_CONSTANT_ARRAY:
3287        *return_value = *value;
3288        zval_copy_ctor(return_value);
3289        return_value->type = IS_ARRAY;
3290        break;
3291
3292    default:
3293        RETURN_NULL();
3294    }
3295}
3296/* }}} */
3297/* {{{ proto int xcache_get_type(zval value)
3298   XCache internal use only for disassembler to get variable type in engine level */
3299PHP_FUNCTION(xcache_get_type)
3300{
3301    zval *value;
3302
3303    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
3304        return;
3305    }
3306
3307    RETURN_LONG(Z_TYPE_P(value));
3308}
3309/* }}} */
3310/* {{{ proto string xcache_coredump(int op_type) */
3311PHP_FUNCTION(xcache_coredump)
3312{
3313    if (xc_test) {
3314        raise(SIGSEGV);
3315    }
3316    else {
3317        php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
3318    }
3319}
3320/* }}} */
3321/* {{{ proto string xcache_is_autoglobal(string name) */
3322PHP_FUNCTION(xcache_is_autoglobal)
3323{
3324    zval *name;
3325
3326    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
3327        return;
3328    }
3329
3330#ifdef IS_UNICODE
3331    convert_to_unicode(name);
3332#else
3333    convert_to_string(name);
3334#endif
3335
3336    RETURN_BOOL(zend_u_hash_exists(CG(auto_globals), UG(unicode), Z_STRVAL_P(name), Z_STRLEN_P(name) + 1));
3337}
3338/* }}} */
3339static zend_function_entry xcache_functions[] = /* {{{ */
3340{
3341    PHP_FE(xcache_count,             NULL)
3342    PHP_FE(xcache_info,              NULL)
3343    PHP_FE(xcache_list,              NULL)
3344    PHP_FE(xcache_clear_cache,       NULL)
3345    PHP_FE(xcache_coredump,          NULL)
3346#ifdef HAVE_XCACHE_ASSEMBLER
3347    PHP_FE(xcache_asm,               NULL)
3348#endif
3349#ifdef HAVE_XCACHE_ENCODER
3350    PHP_FE(xcache_encode,            NULL)
3351#endif
3352#ifdef HAVE_XCACHE_DECODER
3353    PHP_FE(xcache_decode_file,       NULL)
3354    PHP_FE(xcache_decode_string,     NULL)
3355#endif
3356    PHP_FE(xcache_get_special_value, NULL)
3357    PHP_FE(xcache_get_type,          NULL)
3358    PHP_FE(xcache_get_op_type,       NULL)
3359    PHP_FE(xcache_get_data_type,     NULL)
3360    PHP_FE(xcache_get_opcode,        NULL)
3361    PHP_FE(xcache_get_opcode_spec,   NULL)
3362    PHP_FE(xcache_is_autoglobal,     NULL)
3363    PHP_FE(xcache_inc,               NULL)
3364    PHP_FE(xcache_dec,               NULL)
3365    PHP_FE(xcache_get,               NULL)
3366    PHP_FE(xcache_set,               NULL)
3367    PHP_FE(xcache_isset,             NULL)
3368    PHP_FE(xcache_unset,             NULL)
3369    PHP_FE(xcache_unset_by_prefix,   NULL)
3370    PHP_FE(xcache_get_refcount,      NULL)
3371    PHP_FE(xcache_get_isref,         arginfo_xcache_get_isref)
3372#ifdef HAVE_XCACHE_DPRINT
3373    PHP_FE(xcache_dprint,            NULL)
3374#endif
3375    PHP_FE_END
3376};
3377/* }}} */
3378
3379#ifdef ZEND_WIN32
3380#include "dbghelp.h"
3381typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
3382        CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
3383        CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
3384        CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
3385        );
3386
3387static PTOP_LEVEL_EXCEPTION_FILTER oldFilter = NULL;
3388static HMODULE dbghelpModule = NULL;
3389static char crash_dumpPath[_MAX_PATH] = { 0 };
3390static MINIDUMPWRITEDUMP dbghelp_MiniDumpWriteDump = NULL;
3391
3392static LONG WINAPI miniDumperFilter(struct _EXCEPTION_POINTERS *pExceptionInfo) /* {{{ */
3393{
3394    HANDLE fileHandle;
3395
3396    SetUnhandledExceptionFilter(oldFilter);
3397
3398    /* create the file */
3399    fileHandle = CreateFile(crash_dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3400
3401    if (fileHandle != INVALID_HANDLE_VALUE) {
3402        MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
3403        BOOL ok;
3404
3405        exceptionInformation.ThreadId = GetCurrentThreadId();
3406        exceptionInformation.ExceptionPointers = pExceptionInfo;
3407        exceptionInformation.ClientPointers = FALSE;
3408
3409        /* write the dump */
3410        ok = dbghelp_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), fileHandle, MiniDumpNormal|MiniDumpWithDataSegs|MiniDumpWithIndirectlyReferencedMemory, &exceptionInformation, NULL, NULL);
3411        CloseHandle(fileHandle);
3412        if (ok) {
3413            zend_error(E_ERROR, "Saved dump file to '%s'", crash_dumpPath);
3414            return EXCEPTION_EXECUTE_HANDLER;
3415        }
3416        else {
3417            zend_error(E_ERROR, "Failed to save dump file to '%s' (error %d)", crash_dumpPath, GetLastError());
3418        }
3419    }
3420    else {
3421        zend_error(E_ERROR, "Failed to create dump file '%s' (error %d)", crash_dumpPath, GetLastError());
3422    }
3423
3424    return EXCEPTION_CONTINUE_SEARCH;
3425}
3426/* }}} */
3427
3428static void xcache_restore_crash_handler() /* {{{ */
3429{
3430    if (oldFilter) {
3431        SetUnhandledExceptionFilter(oldFilter);
3432        oldFilter = NULL;
3433    }
3434}
3435/* }}} */
3436static void xcache_init_crash_handler() /* {{{ */
3437{
3438    /* firstly see if dbghelp.dll is around and has the function we need
3439       look next to the EXE first, as the one in System32 might be old
3440       (e.g. Windows 2000) */
3441    char dbghelpPath[_MAX_PATH];
3442
3443    if (GetModuleFileName(NULL, dbghelpPath, _MAX_PATH)) {
3444        char *slash = strchr(dbghelpPath, '\\');
3445        if (slash) {
3446            strcpy(slash + 1, "DBGHELP.DLL");
3447            dbghelpModule = LoadLibrary(dbghelpPath);
3448        }
3449    }
3450
3451    if (!dbghelpModule) {
3452        /* load any version we can */
3453        dbghelpModule = LoadLibrary("DBGHELP.DLL");
3454        if (!dbghelpModule) {
3455            zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL not found");
3456            return;
3457        }
3458    }
3459
3460    dbghelp_MiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(dbghelpModule, "MiniDumpWriteDump");
3461    if (!dbghelp_MiniDumpWriteDump) {
3462        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");
3463        return;
3464    }
3465
3466#ifdef XCACHE_VERSION_REVISION
3467#define REVISION "r" XCACHE_VERSION_REVISION
3468#else
3469#define REVISION ""
3470#endif
3471    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());
3472#undef REVISION
3473
3474    oldFilter = SetUnhandledExceptionFilter(&miniDumperFilter);
3475}
3476/* }}} */
3477#else
3478/* old signal handlers {{{ */
3479typedef void (*xc_sighandler_t)(int);
3480#define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
3481#include "util/xc_foreachcoresig.h"
3482#undef FOREACH_SIG
3483/* }}} */
3484static void xcache_signal_handler(int sig);
3485static void xcache_restore_crash_handler() /* {{{ */
3486{
3487#define FOREACH_SIG(sig) do { \
3488    if (old_##sig##_handler != xcache_signal_handler) { \
3489        signal(sig, old_##sig##_handler); \
3490    } \
3491    else { \
3492        signal(sig, SIG_DFL); \
3493    } \
3494} while (0)
3495#include "util/xc_foreachcoresig.h"
3496#undef FOREACH_SIG
3497}
3498/* }}} */
3499static void xcache_init_crash_handler() /* {{{ */
3500{
3501#define FOREACH_SIG(sig) \
3502    old_##sig##_handler = signal(sig, xcache_signal_handler)
3503#include "util/xc_foreachcoresig.h"
3504#undef FOREACH_SIG
3505}
3506/* }}} */
3507static void xcache_signal_handler(int sig) /* {{{ */
3508{
3509    xcache_restore_crash_handler();
3510    if (xc_coredump_dir && xc_coredump_dir[0]) {
3511        if (chdir(xc_coredump_dir) != 0) {
3512            /* error, but nothing can do about it
3513             * and should'nt print anything which might SEGV again */
3514        }
3515    }
3516    if (xc_disable_on_crash) {
3517        xc_disable_on_crash = 0;
3518        if (xc_shm) {
3519            xc_shm->disabled = 1;
3520        }
3521    }
3522    raise(sig);
3523}
3524/* }}} */
3525#endif
3526
3527static startup_func_t xc_last_ext_startup;
3528static int xc_zend_startup_last(zend_extension *extension) /* {{{ */
3529{
3530    zend_extension *ext = zend_get_extension(XCACHE_NAME);
3531    if (ext) {
3532        zend_error(E_WARNING, "Module '" XCACHE_NAME "' already loaded");
3533    }
3534    /* restore */
3535    extension->startup = xc_last_ext_startup;
3536    if (extension->startup) {
3537        if (extension->startup(extension) != SUCCESS) {
3538            return FAILURE;
3539        }
3540    }
3541    assert(xc_llist_zend_extension);
3542    xcache_llist_prepend(&zend_extensions, xc_llist_zend_extension);
3543    return SUCCESS;
3544}
3545/* }}} */
3546static int xc_zend_startup(zend_extension *extension) /* {{{ */
3547{
3548    if (!origin_compile_file) {
3549        origin_compile_file = zend_compile_file;
3550        zend_compile_file = xc_check_initial_compile_file;
3551    }
3552
3553    if (zend_llist_count(&zend_extensions) > 1) {
3554        zend_llist_position lpos;
3555        zend_extension *ext;
3556
3557        xc_llist_zend_extension = xcache_llist_get_element_by_zend_extension(&zend_extensions, XCACHE_NAME);
3558        if (xc_llist_zend_extension != zend_extensions.head) {
3559            zend_error(E_WARNING, "XCache failed to load itself as the first zend_extension. compatibility downgraded");
3560        }
3561        /* hide myself */
3562        xcache_llist_unlink(&zend_extensions, xc_llist_zend_extension);
3563
3564        ext = (zend_extension *) zend_llist_get_last_ex(&zend_extensions, &lpos);
3565        assert(ext && ext != (zend_extension *) xc_llist_zend_extension->data);
3566        xc_last_ext_startup = ext->startup;
3567        ext->startup = xc_zend_startup_last;
3568    }
3569    return SUCCESS;
3570}
3571/* }}} */
3572static void xc_zend_shutdown(zend_extension *extension) /* {{{ */
3573{
3574}
3575/* }}} */
3576/* {{{ zend extension definition structure */
3577static zend_extension zend_extension_entry = {
3578    XCACHE_NAME,
3579    XCACHE_VERSION,
3580    XCACHE_AUTHOR,
3581    XCACHE_URL,
3582    XCACHE_COPYRIGHT,
3583    xc_zend_startup,
3584    xc_zend_shutdown,
3585    NULL,           /* activate_func_t */
3586    NULL,           /* deactivate_func_t */
3587    NULL,           /* message_handler_func_t */
3588    NULL,           /* op_array_handler_func_t */
3589    NULL,           /* statement_handler_func_t */
3590    NULL,           /* fcall_begin_handler_func_t */
3591    NULL,           /* fcall_end_handler_func_t */
3592    NULL,           /* op_array_ctor_func_t */
3593    NULL,           /* op_array_dtor_func_t */
3594    STANDARD_ZEND_EXTENSION_PROPERTIES
3595};
3596/* }}} */
3597
3598/* {{{ PHP_INI */
3599#ifdef ZEND_WIN32
3600#   define DEFAULT_PATH "xcache"
3601#else
3602#   define DEFAULT_PATH "/dev/zero"
3603#endif
3604PHP_INI_BEGIN()
3605    PHP_INI_ENTRY1     ("xcache.mmap_path",     DEFAULT_PATH, PHP_INI_SYSTEM, xcache_OnUpdateString,   &xc_mmap_path)
3606    PHP_INI_ENTRY1     ("xcache.coredump_directory",      "", PHP_INI_SYSTEM, xcache_OnUpdateString,   &xc_coredump_dir)
3607    PHP_INI_ENTRY1     ("xcache.disable_on_crash",       "0", PHP_INI_SYSTEM, xcache_OnUpdateBool,     &xc_disable_on_crash)
3608    PHP_INI_ENTRY1     ("xcache.test",                   "0", PHP_INI_SYSTEM, xcache_OnUpdateBool,     &xc_test)
3609    PHP_INI_ENTRY1     ("xcache.readonly_protection",    "0", PHP_INI_SYSTEM, xcache_OnUpdateBool,     &xc_readonly_protection)
3610    /* opcode cache */
3611    PHP_INI_ENTRY1     ("xcache.size",                   "0", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3612    PHP_INI_ENTRY1     ("xcache.count",                  "1", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3613    PHP_INI_ENTRY1     ("xcache.slots",                 "8K", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3614    PHP_INI_ENTRY1     ("xcache.shm_scheme",          "mmap", PHP_INI_SYSTEM, xcache_OnUpdateString,   &xc_shm_scheme)
3615    PHP_INI_ENTRY1     ("xcache.ttl",                    "0", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_php_ttl)
3616    PHP_INI_ENTRY1     ("xcache.gc_interval",            "0", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_php_gc_interval)
3617    /* var cache */
3618    PHP_INI_ENTRY1     ("xcache.var_size",               "0", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3619    PHP_INI_ENTRY1     ("xcache.var_count",              "1", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3620    PHP_INI_ENTRY1     ("xcache.var_slots",             "8K", PHP_INI_SYSTEM, xcache_OnUpdateDummy,    NULL)
3621    PHP_INI_ENTRY1     ("xcache.var_maxttl",             "0", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_var_maxttl)
3622    PHP_INI_ENTRY1     ("xcache.var_gc_interval",      "120", PHP_INI_SYSTEM, xcache_OnUpdateULong,    &xc_var_gc_interval)
3623
3624    STD_PHP_INI_BOOLEAN("xcache.cacher",                 "1", PHP_INI_ALL,    OnUpdateBool,        cacher,            zend_xcache_globals, xcache_globals)
3625    STD_PHP_INI_BOOLEAN("xcache.stat",                   "1", PHP_INI_ALL,    OnUpdateBool,        stat,              zend_xcache_globals, xcache_globals)
3626    STD_PHP_INI_BOOLEAN("xcache.admin.enable_auth",      "1", PHP_INI_SYSTEM, OnUpdateBool,        auth_enabled,      zend_xcache_globals, xcache_globals)
3627    STD_PHP_INI_BOOLEAN("xcache.experimental",           "0", PHP_INI_ALL,    OnUpdateBool,        experimental,      zend_xcache_globals, xcache_globals)
3628    STD_PHP_INI_ENTRY  ("xcache.var_ttl",                "0", PHP_INI_ALL,    OnUpdateLong,        var_ttl,           zend_xcache_globals, xcache_globals)
3629PHP_INI_END()
3630/* }}} */
3631static PHP_MINFO_FUNCTION(xcache) /* {{{ */
3632{
3633    char buf[100];
3634    char *ptr;
3635    int left, len;
3636    xc_shm_scheme_t *scheme;
3637
3638    php_info_print_table_start();
3639    php_info_print_table_row(2, "XCache Version", XCACHE_VERSION);
3640#ifdef XCACHE_VERSION_REVISION
3641    php_info_print_table_row(2, "Revision", "r" XCACHE_VERSION_REVISION);
3642#endif
3643    php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
3644    php_info_print_table_row(2, "Readonly Protection", xc_readonly_protection ? "enabled" : "disabled");
3645#ifdef ZEND_ENGINE_2_1
3646    ptr = php_format_date("Y-m-d H:i:s", sizeof("Y-m-d H:i:s") - 1, xc_init_time, 1 TSRMLS_CC);
3647    php_info_print_table_row(2, "Cache Init Time", ptr);
3648    efree(ptr);
3649#else
3650    snprintf(buf, sizeof(buf), "%lu", (long unsigned) xc_init_time);
3651    php_info_print_table_row(2, "Cache Init Time", buf);
3652#endif
3653
3654#ifdef ZTS
3655    snprintf(buf, sizeof(buf), "%lu.%lu", xc_init_instance_id, xc_init_instance_subid);
3656#else
3657    snprintf(buf, sizeof(buf), "%lu", xc_init_instance_id);
3658#endif
3659    php_info_print_table_row(2, "Cache Instance Id", buf);
3660
3661    if (xc_php_size) {
3662        ptr = _php_math_number_format(xc_php_size, 0, '.', ',');
3663        snprintf(buf, sizeof(buf), "enabled, %s bytes, %lu split(s), with %lu slots each", ptr, xc_php_hcache.size, xc_php_hentry.size);
3664        php_info_print_table_row(2, "Opcode Cache", buf);
3665        efree(ptr);
3666    }
3667    else {
3668        php_info_print_table_row(2, "Opcode Cache", "disabled");
3669    }
3670    if (xc_var_size) {
3671        ptr = _php_math_number_format(xc_var_size, 0, '.', ',');
3672        snprintf(buf, sizeof(buf), "enabled, %s bytes, %lu split(s), with %lu slots each", ptr, xc_var_hcache.size, xc_var_hentry.size);
3673        php_info_print_table_row(2, "Variable Cache", buf);
3674        efree(ptr);
3675    }
3676    else {
3677        php_info_print_table_row(2, "Variable Cache", "disabled");
3678    }
3679
3680    left = sizeof(buf);
3681    ptr = buf;
3682    buf[0] = '\0';
3683    for (scheme = xc_shm_scheme_first(); scheme; scheme = xc_shm_scheme_next(scheme)) {
3684        len = snprintf(ptr, left, ptr == buf ? "%s" : ", %s", xc_shm_scheme_name(scheme));
3685        left -= len;
3686        ptr += len;
3687    }
3688    php_info_print_table_row(2, "Shared Memory Schemes", buf);
3689
3690    php_info_print_table_end();
3691
3692    DISPLAY_INI_ENTRIES();
3693}
3694/* }}} */
3695static int xc_config_hash(xc_hash_t *p, char *name, char *default_value) /* {{{ */
3696{
3697    size_t bits, size;
3698    char *value;
3699
3700    if (cfg_get_string(name, &value) != SUCCESS) {
3701        value = default_value;
3702    }
3703
3704    p->size = zend_atoi(value, strlen(value));
3705    for (size = 1, bits = 1; size < p->size; bits ++, size <<= 1) {
3706        /* empty body */
3707    }
3708    p->size = size;
3709    p->bits = bits;
3710    p->mask = size - 1;
3711
3712    return SUCCESS;
3713}
3714/* }}} */
3715static int xc_config_long(zend_ulong *p, char *name, char *default_value) /* {{{ */
3716{
3717    char *value;
3718
3719    if (cfg_get_string(name, &value) != SUCCESS) {
3720        value = default_value;
3721    }
3722
3723    *p = zend_atol(value, strlen(value));
3724    return SUCCESS;
3725}
3726/* }}} */
3727static PHP_MINIT_FUNCTION(xcache) /* {{{ */
3728{
3729    char *env;
3730    zend_extension *ext;
3731    zend_llist_position lpos;
3732
3733    xcache_zend_extension_register(&zend_extension_entry, 1);
3734    ext = zend_get_extension("Zend Optimizer");
3735    if (ext) {
3736        /* zend_optimizer.optimization_level>0 is not compatible with other cacher, disabling */
3737        ext->op_array_handler = NULL;
3738    }
3739    /* cache if there's an op_array_ctor */
3740    for (ext = zend_llist_get_first_ex(&zend_extensions, &lpos);
3741            ext;
3742            ext = zend_llist_get_next_ex(&zend_extensions, &lpos)) {
3743        if (ext->op_array_ctor) {
3744            xc_have_op_array_ctor = 1;
3745            break;
3746        }
3747    }
3748
3749
3750#ifndef PHP_GINIT
3751    ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
3752#endif
3753    REGISTER_INI_ENTRIES();
3754
3755    /* additional_functions requires PHP 5.3. TODO: find simpler way to do it */
3756#ifdef ZEND_ENGINE_2_3
3757    if (strcmp(sapi_module.name, "cgi-fcgi") == 0 && !sapi_module.additional_functions && !getenv("XCACHE_SKIP_FCGI_WARNING") && !getenv("GATEWAY_INTERFACE")) {
3758        if ((getenv("PHP_FCGI_CHILDREN") == NULL) || (atoi(getenv("PHP_FCGI_CHILDREN")) < 1)) {
3759            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");
3760        }
3761    }
3762#endif
3763
3764    xc_config_long(&xc_php_size,       "xcache.size",        "0");
3765    xc_config_hash(&xc_php_hcache,     "xcache.count",       "1");
3766    xc_config_hash(&xc_php_hentry,     "xcache.slots",      "8K");
3767
3768    xc_config_long(&xc_var_size,       "xcache.var_size",    "0");
3769    xc_config_hash(&xc_var_hcache,     "xcache.var_count",   "1");
3770    xc_config_hash(&xc_var_hentry,     "xcache.var_slots",  "8K");
3771
3772    if (strcmp(sapi_module.name, "cli") == 0) {
3773        if ((env = getenv("XCACHE_TEST")) != NULL) {
3774            xc_test = atoi(env);
3775        }
3776        if (!xc_test) {
3777            /* disable cache for cli except for testing */
3778            xc_php_size = xc_var_size = 0;
3779        }
3780    }
3781
3782    if (xc_php_size <= 0) {
3783        xc_php_size = xc_php_hcache.size = 0;
3784    }
3785    if (xc_var_size <= 0) {
3786        xc_var_size = xc_var_hcache.size = 0;
3787    }
3788
3789    if (xc_coredump_dir && xc_coredump_dir[0]) {
3790        xcache_init_crash_handler();
3791    }
3792
3793    xc_init_constant(module_number TSRMLS_CC);
3794    xc_shm_init_modules();
3795
3796    if ((xc_php_size || xc_var_size) && xc_mmap_path && xc_mmap_path[0]) {
3797        if (xc_init(module_number TSRMLS_CC) != SUCCESS) {
3798            zend_error(E_ERROR, "XCache: Cannot init");
3799            goto err_init;
3800        }
3801        xc_initized = 1;
3802        xc_init_time = time(NULL);
3803#ifdef PHP_WIN32
3804        xc_init_instance_id = GetCurrentProcessId();
3805#else
3806        xc_init_instance_id = getpid();
3807#endif
3808#ifdef ZTS
3809        xc_init_instance_subid = tsrm_thread_id();
3810#endif
3811    }
3812
3813#ifdef HAVE_XCACHE_OPTIMIZER
3814    xc_optimizer_startup_module();
3815#endif
3816#ifdef HAVE_XCACHE_COVERAGER
3817    xc_coverager_startup_module();
3818#endif
3819#ifdef HAVE_XCACHE_DISASSEMBLER
3820    xc_disassembler_startup_module();
3821#endif
3822    xc_sandbox_module_init(module_number TSRMLS_CC);
3823    return SUCCESS;
3824
3825err_init:
3826    return FAILURE;
3827}
3828/* }}} */
3829/* {{{ PHP_MSHUTDOWN_FUNCTION(xcache) */
3830static PHP_MSHUTDOWN_FUNCTION(xcache)
3831{
3832    xc_sandbox_module_shutdown();
3833
3834    if (xc_initized) {
3835        xc_destroy();
3836    }
3837    if (xc_mmap_path) {
3838        pefree(xc_mmap_path, 1);
3839        xc_mmap_path = NULL;
3840    }
3841    if (xc_shm_scheme) {
3842        pefree(xc_shm_scheme, 1);
3843        xc_shm_scheme = NULL;
3844    }
3845
3846    if (xc_coredump_dir && xc_coredump_dir[0]) {
3847        xcache_restore_crash_handler();
3848    }
3849    if (xc_coredump_dir) {
3850        pefree(xc_coredump_dir, 1);
3851        xc_coredump_dir = NULL;
3852    }
3853#ifndef PHP_GINIT
3854#   ifdef ZTS
3855    ts_free_id(xcache_globals_id);
3856#   else
3857    xc_shutdown_globals(&xcache_globals TSRMLS_CC);
3858#   endif
3859#endif
3860
3861    xcache_zend_extension_unregister(&zend_extension_entry);
3862    UNREGISTER_INI_ENTRIES();
3863    return SUCCESS;
3864}
3865/* }}} */
3866/* {{{ PHP_RINIT_FUNCTION(xcache) */
3867static PHP_RINIT_FUNCTION(xcache)
3868{
3869    xc_request_init(TSRMLS_C);
3870    return SUCCESS;
3871}
3872/* }}} */
3873/* {{{ PHP_RSHUTDOWN_FUNCTION(xcache) */
3874#ifndef ZEND_ENGINE_2
3875static PHP_RSHUTDOWN_FUNCTION(xcache)
3876#else
3877static ZEND_MODULE_POST_ZEND_DEACTIVATE_D(xcache)
3878#endif
3879{
3880#ifdef ZEND_ENGINE_2
3881    TSRMLS_FETCH();
3882#endif
3883
3884    xc_request_shutdown(TSRMLS_C);
3885    return SUCCESS;
3886}
3887/* }}} */
3888/* {{{ module dependencies */
3889#ifdef STANDARD_MODULE_HEADER_EX
3890static zend_module_dep xcache_module_deps[] = {
3891    ZEND_MOD_REQUIRED("standard")
3892    ZEND_MOD_CONFLICTS("apc")
3893    ZEND_MOD_CONFLICTS("eAccelerator")
3894    ZEND_MOD_CONFLICTS("Turck MMCache")
3895    ZEND_MOD_END
3896};
3897#endif
3898/* }}} */ 
3899/* {{{ module definition structure */
3900zend_module_entry xcache_module_entry = {
3901#ifdef STANDARD_MODULE_HEADER_EX
3902    STANDARD_MODULE_HEADER_EX,
3903    NULL,
3904    xcache_module_deps,
3905#else
3906    STANDARD_MODULE_HEADER,
3907#endif
3908    XCACHE_NAME,
3909    xcache_functions,
3910    PHP_MINIT(xcache),
3911    PHP_MSHUTDOWN(xcache),
3912    PHP_RINIT(xcache),
3913#ifndef ZEND_ENGINE_2
3914    PHP_RSHUTDOWN(xcache),
3915#else
3916    NULL,
3917#endif
3918    PHP_MINFO(xcache),
3919    XCACHE_VERSION,
3920#ifdef PHP_GINIT
3921    PHP_MODULE_GLOBALS(xcache),
3922    PHP_GINIT(xcache),
3923    PHP_GSHUTDOWN(xcache),
3924#endif
3925#ifdef ZEND_ENGINE_2
3926    ZEND_MODULE_POST_ZEND_DEACTIVATE_N(xcache),
3927#else
3928    NULL,
3929    NULL,
3930#endif
3931    STANDARD_MODULE_PROPERTIES_EX
3932};
3933
3934#ifdef COMPILE_DL_XCACHE
3935ZEND_GET_MODULE(xcache)
3936#endif
3937/* }}} */
Note: See TracBrowser for help on using the repository browser.