1/*
2   +----------------------------------------------------------------------+
3   | Zend OPcache                                                         |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1998-2015 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Andi Gutmans <andi@zend.com>                                |
16   |          Zeev Suraski <zeev@zend.com>                                |
17   |          Stanislav Malyshev <stas@zend.com>                          |
18   |          Dmitry Stogov <dmitry@zend.com>                             |
19   +----------------------------------------------------------------------+
20*/
21
22#include "main/php.h"
23#include "main/php_globals.h"
24#include "zend.h"
25#include "zend_extensions.h"
26#include "zend_compile.h"
27#include "ZendAccelerator.h"
28#include "zend_persist.h"
29#include "zend_shared_alloc.h"
30#include "zend_accelerator_module.h"
31#include "zend_accelerator_blacklist.h"
32#include "zend_list.h"
33#include "zend_execute.h"
34#include "main/SAPI.h"
35#include "main/php_streams.h"
36#include "main/php_open_temporary_file.h"
37#include "zend_API.h"
38#include "zend_ini.h"
39#include "zend_virtual_cwd.h"
40#include "zend_accelerator_util_funcs.h"
41#include "zend_accelerator_hash.h"
42#include "ext/pcre/php_pcre.h"
43
44#ifndef ZEND_WIN32
45#include  <netdb.h>
46#endif
47
48#ifdef ZEND_WIN32
49typedef int uid_t;
50typedef int gid_t;
51#include <io.h>
52#endif
53
54#ifndef ZEND_WIN32
55# include <sys/time.h>
56#else
57# include <process.h>
58#endif
59
60#ifdef HAVE_UNISTD_H
61# include <unistd.h>
62#endif
63#include <fcntl.h>
64#include <signal.h>
65#include <time.h>
66
67#ifndef ZEND_WIN32
68# include <sys/types.h>
69# include <sys/ipc.h>
70#endif
71
72#include <sys/stat.h>
73#include <errno.h>
74
75#define SHM_PROTECT() \
76    do { \
77        if (ZCG(accel_directives).protect_memory) { \
78            zend_accel_shared_protect(1); \
79        } \
80    } while (0)
81#define SHM_UNPROTECT() \
82    do { \
83        if (ZCG(accel_directives).protect_memory) { \
84            zend_accel_shared_protect(0); \
85        } \
86    } while (0)
87
88ZEND_EXTENSION();
89
90#ifndef ZTS
91zend_accel_globals accel_globals;
92#else
93int accel_globals_id;
94#if defined(COMPILE_DL_OPCACHE)
95ZEND_TSRMLS_CACHE_DEFINE();
96#endif
97#endif
98
99/* Points to the structure shared across all PHP processes */
100zend_accel_shared_globals *accel_shared_globals = NULL;
101
102/* true globals, no need for thread safety */
103zend_bool accel_startup_ok = 0;
104static char *zps_failure_reason = NULL;
105char *zps_api_failure_reason = NULL;
106
107static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type);
108static int (*accelerator_orig_zend_stream_open_function)(const char *filename, zend_file_handle *handle );
109static zend_string *(*accelerator_orig_zend_resolve_path)(const char *filename, int filename_len);
110static void (*orig_chdir)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
111static ZEND_INI_MH((*orig_include_path_on_modify)) = NULL;
112
113#ifdef ZEND_WIN32
114# define INCREMENT(v) InterlockedIncrement64(&ZCSG(v))
115# define DECREMENT(v) InterlockedDecrement64(&ZCSG(v))
116# define LOCKVAL(v)   (ZCSG(v))
117#endif
118
119#ifdef ZEND_WIN32
120static time_t zend_accel_get_time(void)
121{
122    FILETIME now;
123    GetSystemTimeAsFileTime(&now);
124
125    return (time_t) ((((((__int64)now.dwHighDateTime) << 32)|now.dwLowDateTime) - 116444736000000000L)/10000000);
126}
127#else
128# define zend_accel_get_time() time(NULL)
129#endif
130
131static inline int is_stream_path(const char *filename)
132{
133    const char *p;
134
135    for (p = filename;
136         (*p >= 'a' && *p <= 'z') ||
137         (*p >= 'A' && *p <= 'Z') ||
138         (*p >= '0' && *p <= '9') ||
139         *p == '+' || *p == '-' || *p == '.';
140         p++);
141    return ((p != filename) && (p[0] == ':') && (p[1] == '/') && (p[2] == '/'));
142}
143
144static inline int is_cacheable_stream_path(const char *filename)
145{
146    return memcmp(filename, "file://", sizeof("file://") - 1) == 0 ||
147           memcmp(filename, "phar://", sizeof("phar://") - 1) == 0;
148}
149
150/* O+ overrides PHP chdir() function and remembers the current working directory
151 * in ZCG(cwd) and ZCG(cwd_len). Later accel_getcwd() can use stored value and
152 * avoid getcwd() call.
153 */
154static ZEND_FUNCTION(accel_chdir)
155{
156    char cwd[MAXPATHLEN];
157
158    orig_chdir(INTERNAL_FUNCTION_PARAM_PASSTHRU);
159    if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
160        if (ZCG(cwd)) {
161            zend_string_release(ZCG(cwd));
162        }
163        ZCG(cwd) = zend_string_init(cwd, strlen(cwd), 0);
164    } else {
165        if (ZCG(cwd)) {
166            zend_string_release(ZCG(cwd));
167            ZCG(cwd) = NULL;
168        }
169    }
170    ZCG(cwd_key_len) = 0;
171    ZCG(cwd_check) = 1;
172}
173
174static inline zend_string* accel_getcwd(void)
175{
176    if (ZCG(cwd)) {
177        return ZCG(cwd);
178    } else {
179        char cwd[MAXPATHLEN + 1];
180
181        if (!VCWD_GETCWD(cwd, MAXPATHLEN)) {
182            return NULL;
183        }
184        ZCG(cwd) = zend_string_init(cwd, strlen(cwd), 0);
185        ZCG(cwd_key_len) = 0;
186        ZCG(cwd_check) = 1;
187        return ZCG(cwd);
188    }
189}
190
191void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason)
192{
193    if ((((double) ZSMMG(wasted_shared_memory)) / ZCG(accel_directives).memory_consumption) >= ZCG(accel_directives).max_wasted_percentage) {
194        zend_accel_schedule_restart(reason);
195    }
196}
197
198/* O+ tracks changes of "include_path" directive. It stores all the requested
199 * values in ZCG(include_paths) shared hash table, current value in
200 * ZCG(include_path)/ZCG(include_path_len) and one letter "path key" in
201 * ZCG(include_path_key).
202 */
203static ZEND_INI_MH(accel_include_path_on_modify)
204{
205    int ret = orig_include_path_on_modify(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
206
207    if (ret == SUCCESS) {
208        ZCG(include_path) = new_value;
209        ZCG(include_path_key_len) = 0;
210        ZCG(include_path_check) = 1;
211    }
212    return ret;
213}
214
215/* Interned strings support */
216static zend_string *(*orig_new_interned_string)(zend_string *str);
217static void (*orig_interned_strings_snapshot)(void);
218static void (*orig_interned_strings_restore)(void);
219
220/* O+ disables creation of interned strings by regular PHP compiler, instead,
221 * it creates interned strings in shared memory when saves a script.
222 * Such interned strings are shared across all PHP processes
223 */
224static zend_string *accel_new_interned_string_for_php(zend_string *str)
225{
226    return str;
227}
228
229static void accel_interned_strings_snapshot_for_php(void)
230{
231}
232
233static void accel_interned_strings_restore_for_php(void)
234{
235}
236
237#ifndef ZTS
238static void accel_interned_strings_restore_state(void)
239{
240    uint idx = ZCSG(interned_strings).nNumUsed;
241    uint nIndex;
242    Bucket *p;
243
244    ZCSG(interned_strings_top) = ZCSG(interned_strings_saved_top);
245    while (idx > 0) {
246        idx--;
247        p = ZCSG(interned_strings).arData + idx;
248        if ((char*)p->key < ZCSG(interned_strings_top)) break;
249        ZCSG(interned_strings).nNumUsed--;
250        ZCSG(interned_strings).nNumOfElements--;
251
252        nIndex = p->h | ZCSG(interned_strings).nTableMask;
253        if (HT_HASH(&ZCSG(interned_strings), nIndex) == HT_IDX_TO_HASH(idx)) {
254            HT_HASH(&ZCSG(interned_strings), nIndex) = Z_NEXT(p->val);
255        } else {
256            uint32_t prev = HT_HASH(&ZCSG(interned_strings), nIndex);
257            while (Z_NEXT(HT_HASH_TO_BUCKET(&ZCSG(interned_strings), prev)->val) != idx) {
258                prev = Z_NEXT(HT_HASH_TO_BUCKET(&ZCSG(interned_strings), prev)->val);
259            }
260            Z_NEXT(HT_HASH_TO_BUCKET(&ZCSG(interned_strings), prev)->val) = Z_NEXT(p->val);
261        }
262    }
263}
264
265static void accel_interned_strings_save_state(void)
266{
267    ZCSG(interned_strings_saved_top) = ZCSG(interned_strings_top);
268}
269#endif
270
271#ifndef ZTS
272static zend_string *accel_find_interned_string(zend_string *str)
273{
274/* for now interned strings are supported only for non-ZTS build */
275    zend_ulong h;
276    uint nIndex;
277    uint idx;
278    Bucket *arData, *p;
279
280    if (IS_ACCEL_INTERNED(str)) {
281        /* this is already an interned string */
282        return str;
283    }
284
285    h = zend_string_hash_val(str);
286    nIndex = h | ZCSG(interned_strings).nTableMask;
287
288    /* check for existing interned string */
289    idx = HT_HASH(&ZCSG(interned_strings), nIndex);
290    arData = ZCSG(interned_strings).arData;
291    while (idx != HT_INVALID_IDX) {
292        p = HT_HASH_TO_BUCKET_EX(arData, idx);
293        if ((p->h == h) && (p->key->len == str->len)) {
294            if (!memcmp(p->key->val, str->val, str->len)) {
295                return p->key;
296            }
297        }
298        idx = Z_NEXT(p->val);
299    }
300
301    return NULL;
302}
303#endif
304
305zend_string *accel_new_interned_string(zend_string *str)
306{
307/* for now interned strings are supported only for non-ZTS build */
308#ifndef ZTS
309    zend_ulong h;
310    uint nIndex;
311    uint idx;
312    Bucket *p;
313
314    if (IS_ACCEL_INTERNED(str)) {
315        /* this is already an interned string */
316        return str;
317    }
318
319    h = zend_string_hash_val(str);
320    nIndex = h | ZCSG(interned_strings).nTableMask;
321
322    /* check for existing interned string */
323    idx = HT_HASH(&ZCSG(interned_strings), nIndex);
324    while (idx != HT_INVALID_IDX) {
325        p = HT_HASH_TO_BUCKET(&ZCSG(interned_strings), idx);
326        if ((p->h == h) && (p->key->len == str->len)) {
327            if (!memcmp(p->key->val, str->val, str->len)) {
328                zend_string_release(str);
329                return p->key;
330            }
331        }
332        idx = Z_NEXT(p->val);
333    }
334
335    if (ZCSG(interned_strings_top) + ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + str->len + 1) >=
336        ZCSG(interned_strings_end)) {
337        /* no memory, return the same non-interned string */
338        zend_accel_error(ACCEL_LOG_WARNING, "Interned string buffer overflow");
339        return str;
340    }
341
342    /* create new interning string in shared interned strings buffer */
343
344    idx = ZCSG(interned_strings).nNumUsed++;
345    ZCSG(interned_strings).nNumOfElements++;
346    p = ZCSG(interned_strings).arData + idx;
347    p->key = (zend_string*) ZCSG(interned_strings_top);
348    ZCSG(interned_strings_top) += ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + str->len + 1);
349    p->h = h;
350    GC_REFCOUNT(p->key) = 1;
351#if 1
352    /* optimized single assignment */
353    GC_TYPE_INFO(p->key) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << 8);
354#else
355    GC_TYPE(p->key) = IS_STRING;
356    GC_FLAGS(p->key) = IS_STR_INTERNED | IS_STR_PERMANENT;
357#endif
358    p->key->h = str->h;
359    p->key->len = str->len;
360    memcpy(p->key->val, str->val, str->len);
361    ZVAL_INTERNED_STR(&p->val, p->key);
362    Z_NEXT(p->val) = HT_HASH(&ZCSG(interned_strings), nIndex);
363    HT_HASH(&ZCSG(interned_strings), nIndex) = HT_IDX_TO_HASH(idx);
364    zend_string_release(str);
365    return p->key;
366#else
367    return str;
368#endif
369}
370
371#ifndef ZTS
372/* Copy PHP interned strings from PHP process memory into the shared memory */
373static void accel_use_shm_interned_strings(void)
374{
375    uint idx, j;
376    Bucket *p, *q;
377
378    /* empty string */
379    CG(empty_string) = accel_new_interned_string(CG(empty_string));
380    for (j = 0; j < 256; j++) {
381        char s[2];
382        s[0] = j;
383        s[1] = 0;
384        CG(one_char_string)[j] = accel_new_interned_string(zend_string_init(s, 1, 0));
385    }
386
387    /* function table hash keys */
388    for (idx = 0; idx < CG(function_table)->nNumUsed; idx++) {
389        p = CG(function_table)->arData + idx;
390        if (Z_TYPE(p->val) == IS_UNDEF) continue;
391        if (p->key) {
392            p->key = accel_new_interned_string(p->key);
393        }
394        if (Z_FUNC(p->val)->common.function_name) {
395            Z_FUNC(p->val)->common.function_name = accel_new_interned_string(Z_FUNC(p->val)->common.function_name);
396        }
397    }
398
399    /* class table hash keys, class names, properties, methods, constants, etc */
400    for (idx = 0; idx < CG(class_table)->nNumUsed; idx++) {
401        zend_class_entry *ce;
402
403        p = CG(class_table)->arData + idx;
404        if (Z_TYPE(p->val) == IS_UNDEF) continue;
405        ce = (zend_class_entry*)Z_PTR(p->val);
406
407        if (p->key) {
408            p->key = accel_new_interned_string(p->key);
409        }
410
411        if (ce->name) {
412            ce->name = accel_new_interned_string(ce->name);
413        }
414
415        for (j = 0; j < ce->properties_info.nNumUsed; j++) {
416            zend_property_info *info;
417
418            q = ce->properties_info.arData + j;
419            if (Z_TYPE(q->val) == IS_UNDEF) continue;
420
421            info = (zend_property_info*)Z_PTR(q->val);
422
423            if (q->key) {
424                q->key = accel_new_interned_string(q->key);
425            }
426
427            if (info->name) {
428                info->name = accel_new_interned_string(info->name);
429            }
430        }
431
432        for (j = 0; j < ce->function_table.nNumUsed; j++) {
433            q = ce->function_table.arData + j;
434            if (Z_TYPE(q->val) == IS_UNDEF) continue;
435            if (q->key) {
436                q->key = accel_new_interned_string(q->key);
437            }
438            if (Z_FUNC(q->val)->common.function_name) {
439                Z_FUNC(q->val)->common.function_name = accel_new_interned_string(Z_FUNC(q->val)->common.function_name);
440            }
441        }
442
443        for (j = 0; j < ce->constants_table.nNumUsed; j++) {
444            q = ce->constants_table.arData + j;
445            if (!Z_TYPE(q->val) == IS_UNDEF) continue;
446            if (q->key) {
447                q->key = accel_new_interned_string(q->key);
448            }
449        }
450    }
451
452    /* constant hash keys */
453    for (idx = 0; idx < EG(zend_constants)->nNumUsed; idx++) {
454        p = EG(zend_constants)->arData + idx;
455        if (!Z_TYPE(p->val) == IS_UNDEF) continue;
456        if (p->key) {
457            p->key = accel_new_interned_string(p->key);
458        }
459    }
460
461    /* auto globals hash keys and names */
462    for (idx = 0; idx < CG(auto_globals)->nNumUsed; idx++) {
463        zend_auto_global *auto_global;
464
465        p = CG(auto_globals)->arData + idx;
466        if (Z_TYPE(p->val) == IS_UNDEF) continue;
467
468        auto_global = (zend_auto_global*)Z_PTR(p->val);;
469
470        zend_string_addref(auto_global->name);
471        auto_global->name = accel_new_interned_string(auto_global->name);
472        if (p->key) {
473            p->key = accel_new_interned_string(p->key);
474        }
475    }
476}
477#endif
478
479static inline void accel_restart_enter(void)
480{
481#ifdef ZEND_WIN32
482    INCREMENT(restart_in);
483#else
484    static const FLOCK_STRUCTURE(restart_in_progress, F_WRLCK, SEEK_SET, 2, 1);
485
486    if (fcntl(lock_file, F_SETLK, &restart_in_progress) == -1) {
487        zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(+1):  %s (%d)", strerror(errno), errno);
488    }
489#endif
490    ZCSG(restart_in_progress) = 1;
491}
492
493static inline void accel_restart_leave(void)
494{
495#ifdef ZEND_WIN32
496    ZCSG(restart_in_progress) = 0;
497    DECREMENT(restart_in);
498#else
499    static const FLOCK_STRUCTURE(restart_finished, F_UNLCK, SEEK_SET, 2, 1);
500
501    ZCSG(restart_in_progress) = 0;
502    if (fcntl(lock_file, F_SETLK, &restart_finished) == -1) {
503        zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(-1):  %s (%d)", strerror(errno), errno);
504    }
505#endif
506}
507
508static inline int accel_restart_is_active(void)
509{
510    if (ZCSG(restart_in_progress)) {
511#ifndef ZEND_WIN32
512        FLOCK_STRUCTURE(restart_check, F_WRLCK, SEEK_SET, 2, 1);
513
514        if (fcntl(lock_file, F_GETLK, &restart_check) == -1) {
515            zend_accel_error(ACCEL_LOG_DEBUG, "RestartC:  %s (%d)", strerror(errno), errno);
516            return FAILURE;
517        }
518        if (restart_check.l_type == F_UNLCK) {
519            ZCSG(restart_in_progress) = 0;
520            return 0;
521        } else {
522            return 1;
523        }
524#else
525        return LOCKVAL(restart_in) != 0;
526#endif
527    }
528    return 0;
529}
530
531/* Creates a read lock for SHM access */
532static inline void accel_activate_add(void)
533{
534#ifdef ZEND_WIN32
535    INCREMENT(mem_usage);
536#else
537    static const FLOCK_STRUCTURE(mem_usage_lock, F_RDLCK, SEEK_SET, 1, 1);
538
539    if (fcntl(lock_file, F_SETLK, &mem_usage_lock) == -1) {
540        zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(+1):  %s (%d)", strerror(errno), errno);
541    }
542#endif
543}
544
545/* Releases a lock for SHM access */
546static inline void accel_deactivate_sub(void)
547{
548#ifdef ZEND_WIN32
549    if (ZCG(counted)) {
550        DECREMENT(mem_usage);
551        ZCG(counted) = 0;
552    }
553#else
554    static const FLOCK_STRUCTURE(mem_usage_unlock, F_UNLCK, SEEK_SET, 1, 1);
555
556    if (fcntl(lock_file, F_SETLK, &mem_usage_unlock) == -1) {
557        zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(-1):  %s (%d)", strerror(errno), errno);
558    }
559#endif
560}
561
562static inline void accel_unlock_all(void)
563{
564#ifdef ZEND_WIN32
565    accel_deactivate_sub();
566#else
567    static const FLOCK_STRUCTURE(mem_usage_unlock_all, F_UNLCK, SEEK_SET, 0, 0);
568
569    if (fcntl(lock_file, F_SETLK, &mem_usage_unlock_all) == -1) {
570        zend_accel_error(ACCEL_LOG_DEBUG, "UnlockAll:  %s (%d)", strerror(errno), errno);
571    }
572#endif
573}
574
575#ifndef ZEND_WIN32
576static inline void kill_all_lockers(struct flock *mem_usage_check)
577{
578    int tries = 10;
579
580    /* so that other process won't try to force while we are busy cleaning up */
581    ZCSG(force_restart_time) = 0;
582    while (mem_usage_check->l_pid > 0) {
583        while (tries--) {
584            zend_accel_error(ACCEL_LOG_ERROR, "Killed locker %d", mem_usage_check->l_pid);
585            if (kill(mem_usage_check->l_pid, SIGKILL)) {
586                break;
587            }
588            /* give it a chance to die */
589            usleep(20000);
590            if (kill(mem_usage_check->l_pid, 0)) {
591                /* can't kill it */
592                break;
593            }
594            usleep(10000);
595        }
596        if (!tries) {
597            zend_accel_error(ACCEL_LOG_ERROR, "Can't kill %d after 20 tries!", mem_usage_check->l_pid);
598            ZCSG(force_restart_time) = time(NULL); /* restore forced restart request */
599        }
600
601        mem_usage_check->l_type = F_WRLCK;
602        mem_usage_check->l_whence = SEEK_SET;
603        mem_usage_check->l_start = 1;
604        mem_usage_check->l_len = 1;
605        mem_usage_check->l_pid = -1;
606        if (fcntl(lock_file, F_GETLK, mem_usage_check) == -1) {
607            zend_accel_error(ACCEL_LOG_DEBUG, "KLockers:  %s (%d)", strerror(errno), errno);
608            break;
609        }
610
611        if (mem_usage_check->l_type == F_UNLCK || mem_usage_check->l_pid <= 0) {
612            break;
613        }
614    }
615}
616#endif
617
618static inline int accel_is_inactive(void)
619{
620#ifdef ZEND_WIN32
621    if (LOCKVAL(mem_usage) == 0) {
622        return SUCCESS;
623    }
624#else
625    FLOCK_STRUCTURE(mem_usage_check, F_WRLCK, SEEK_SET, 1, 1);
626
627    mem_usage_check.l_pid = -1;
628    if (fcntl(lock_file, F_GETLK, &mem_usage_check) == -1) {
629        zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC:  %s (%d)", strerror(errno), errno);
630        return FAILURE;
631    }
632    if (mem_usage_check.l_type == F_UNLCK) {
633        return SUCCESS;
634    }
635
636    if (ZCG(accel_directives).force_restart_timeout
637        && ZCSG(force_restart_time)
638        && time(NULL) >= ZCSG(force_restart_time)) {
639        zend_accel_error(ACCEL_LOG_WARNING, "Forced restart at %d (after %d seconds), locked by %d", time(NULL), ZCG(accel_directives).force_restart_timeout, mem_usage_check.l_pid);
640        kill_all_lockers(&mem_usage_check);
641
642        return FAILURE; /* next request should be able to restart it */
643    }
644#endif
645
646    return FAILURE;
647}
648
649static int zend_get_stream_timestamp(const char *filename, zend_stat_t *statbuf)
650{
651    php_stream_wrapper *wrapper;
652    php_stream_statbuf stream_statbuf;
653    int ret, er;
654
655    if (!filename) {
656        return FAILURE;
657    }
658
659    wrapper = php_stream_locate_url_wrapper(filename, NULL, STREAM_LOCATE_WRAPPERS_ONLY);
660    if (!wrapper) {
661        return FAILURE;
662    }
663    if (!wrapper->wops || !wrapper->wops->url_stat) {
664        statbuf->st_mtime = 1;
665        return SUCCESS; /* anything other than 0 is considered to be a valid timestamp */
666    }
667
668    er = EG(error_reporting);
669    EG(error_reporting) = 0;
670    zend_try {
671        ret = wrapper->wops->url_stat(wrapper, (char*)filename, PHP_STREAM_URL_STAT_QUIET, &stream_statbuf, NULL);
672    } zend_catch {
673        ret = -1;
674    } zend_end_try();
675    EG(error_reporting) = er;
676
677    if (ret != 0) {
678        return FAILURE;
679    }
680
681    *statbuf = stream_statbuf.sb;
682    return SUCCESS;
683}
684
685#if ZEND_WIN32
686static accel_time_t zend_get_file_handle_timestamp_win(zend_file_handle *file_handle, size_t *size)
687{
688    static unsigned __int64 utc_base = 0;
689    static FILETIME utc_base_ft;
690    WIN32_FILE_ATTRIBUTE_DATA fdata;
691
692    if (!file_handle->opened_path) {
693        return 0;
694    }
695
696    if (!utc_base) {
697        SYSTEMTIME st;
698
699        st.wYear = 1970;
700        st.wMonth = 1;
701        st.wDay = 1;
702        st.wHour = 0;
703        st.wMinute = 0;
704        st.wSecond = 0;
705        st.wMilliseconds = 0;
706
707        SystemTimeToFileTime (&st, &utc_base_ft);
708        utc_base = (((unsigned __int64)utc_base_ft.dwHighDateTime) << 32) + utc_base_ft.dwLowDateTime;
709    }
710
711    if (file_handle->opened_path && GetFileAttributesEx(file_handle->opened_path->val, GetFileExInfoStandard, &fdata) != 0) {
712        unsigned __int64 ftime;
713
714        if (CompareFileTime (&fdata.ftLastWriteTime, &utc_base_ft) < 0) {
715            return 0;
716        }
717
718        ftime = (((unsigned __int64)fdata.ftLastWriteTime.dwHighDateTime) << 32) + fdata.ftLastWriteTime.dwLowDateTime - utc_base;
719        ftime /= 10000000L;
720
721        if (size) {
722            *size = (size_t)((((unsigned __int64)fdata.nFileSizeHigh) << 32) + (unsigned __int64)fdata.nFileSizeLow);
723        }
724        return (accel_time_t)ftime;
725    }
726    return 0;
727}
728#endif
729
730static accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size)
731{
732    zend_stat_t statbuf;
733#ifdef ZEND_WIN32
734    accel_time_t res;
735#endif
736
737    if (sapi_module.get_stat &&
738        !EG(current_execute_data) &&
739        file_handle->filename == SG(request_info).path_translated) {
740
741        zend_stat_t *tmpbuf = sapi_module.get_stat();
742
743        if (tmpbuf) {
744            if (size) {
745                *size = tmpbuf->st_size;
746            }
747            return tmpbuf->st_mtime;
748        }
749    }
750
751#ifdef ZEND_WIN32
752    res = zend_get_file_handle_timestamp_win(file_handle, size);
753    if (res) {
754        return res;
755    }
756#endif
757
758    switch (file_handle->type) {
759        case ZEND_HANDLE_FD:
760            if (zend_fstat(file_handle->handle.fd, &statbuf) == -1) {
761                return 0;
762            }
763            break;
764        case ZEND_HANDLE_FP:
765            if (zend_fstat(fileno(file_handle->handle.fp), &statbuf) == -1) {
766                if (zend_get_stream_timestamp(file_handle->filename, &statbuf) != SUCCESS) {
767                    return 0;
768                }
769            }
770            break;
771        case ZEND_HANDLE_FILENAME:
772        case ZEND_HANDLE_MAPPED:
773            if (file_handle->opened_path) {
774                char *file_path = file_handle->opened_path->val;
775
776                if (is_stream_path(file_path)) {
777                    if (zend_get_stream_timestamp(file_path, &statbuf) == SUCCESS) {
778                        break;
779                    }
780                }
781                if (VCWD_STAT(file_path, &statbuf) != -1) {
782                    break;
783                }
784            }
785
786            if (zend_get_stream_timestamp(file_handle->filename, &statbuf) != SUCCESS) {
787                return 0;
788            }
789            break;
790        case ZEND_HANDLE_STREAM:
791            {
792                php_stream *stream = (php_stream *)file_handle->handle.stream.handle;
793                php_stream_statbuf sb;
794                int ret, er;
795
796                if (!stream ||
797                    !stream->ops ||
798                    !stream->ops->stat) {
799                    return 0;
800                }
801
802                er = EG(error_reporting);
803                EG(error_reporting) = 0;
804                zend_try {
805                    ret = stream->ops->stat(stream, &sb);
806                } zend_catch {
807                    ret = -1;
808                } zend_end_try();
809                EG(error_reporting) = er;
810                if (ret != 0) {
811                    return 0;
812                }
813
814                statbuf = sb.sb;
815            }
816            break;
817
818        default:
819            return 0;
820    }
821
822    if (size) {
823        *size = statbuf.st_size;
824    }
825    return statbuf.st_mtime;
826}
827
828static inline int do_validate_timestamps(zend_persistent_script *persistent_script, zend_file_handle *file_handle)
829{
830    zend_file_handle ps_handle;
831    zend_string *full_path_ptr = NULL;
832
833    /** check that the persistent script is indeed the same file we cached
834     * (if part of the path is a symlink than it possible that the user will change it)
835     * See bug #15140
836     */
837    if (file_handle->opened_path) {
838        if (persistent_script->full_path != file_handle->opened_path &&
839            (persistent_script->full_path->len != file_handle->opened_path->len ||
840             memcmp(persistent_script->full_path->val, file_handle->opened_path->val, file_handle->opened_path->len) != 0)) {
841            return FAILURE;
842        }
843    } else {
844        full_path_ptr = accelerator_orig_zend_resolve_path(file_handle->filename, strlen(file_handle->filename));
845        if (full_path_ptr &&
846            persistent_script->full_path != full_path_ptr &&
847            (persistent_script->full_path->len != full_path_ptr->len ||
848             memcmp(persistent_script->full_path->val, full_path_ptr->val, full_path_ptr->len) != 0)) {
849            zend_string_release(full_path_ptr);
850            return FAILURE;
851        }
852        file_handle->opened_path = full_path_ptr;
853    }
854
855    if (persistent_script->timestamp == 0) {
856        if (full_path_ptr) {
857            zend_string_release(full_path_ptr);
858            file_handle->opened_path = NULL;
859        }
860        return FAILURE;
861    }
862
863    if (zend_get_file_handle_timestamp(file_handle, NULL) == persistent_script->timestamp) {
864        if (full_path_ptr) {
865            zend_string_release(full_path_ptr);
866            file_handle->opened_path = NULL;
867        }
868        return SUCCESS;
869    }
870    if (full_path_ptr) {
871        zend_string_release(full_path_ptr);
872        file_handle->opened_path = NULL;
873    }
874
875    ps_handle.type = ZEND_HANDLE_FILENAME;
876    ps_handle.filename = persistent_script->full_path->val;
877    ps_handle.opened_path = persistent_script->full_path;
878
879    if (zend_get_file_handle_timestamp(&ps_handle, NULL) == persistent_script->timestamp) {
880        return SUCCESS;
881    }
882
883    return FAILURE;
884}
885
886int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle)
887{
888    if (ZCG(accel_directives).revalidate_freq &&
889        persistent_script->dynamic_members.revalidate >= ZCG(request_time)) {
890        return SUCCESS;
891    } else if (do_validate_timestamps(persistent_script, file_handle) == FAILURE) {
892        return FAILURE;
893    } else {
894        persistent_script->dynamic_members.revalidate = ZCG(request_time) + ZCG(accel_directives).revalidate_freq;
895        return SUCCESS;
896    }
897}
898
899static unsigned int zend_accel_script_checksum(zend_persistent_script *persistent_script)
900{
901    signed char *mem = (signed char*)persistent_script->mem;
902    size_t size = persistent_script->size;
903    size_t persistent_script_check_block_size = ((char *)&(persistent_script->dynamic_members)) - (char *)persistent_script;
904    unsigned int checksum = ADLER32_INIT;
905
906    if (mem < (signed char*)persistent_script) {
907        checksum = zend_adler32(checksum, mem, (signed char*)persistent_script - mem);
908        size -= (signed char*)persistent_script - mem;
909        mem  += (signed char*)persistent_script - mem;
910    }
911
912    zend_adler32(checksum, mem, persistent_script_check_block_size);
913    mem  += sizeof(*persistent_script);
914    size -= sizeof(*persistent_script);
915
916    if (size > 0) {
917        checksum = zend_adler32(checksum, mem, size);
918    }
919    return checksum;
920}
921
922/* Instead of resolving full real path name each time we need to identify file,
923 * we create a key that consist from requested file name, current working
924 * directory, current include_path, etc */
925char *accel_make_persistent_key(const char *path, int path_length, int *key_len)
926{
927    int key_length;
928
929    /* CWD and include_path don't matter for absolute file names and streams */
930    if (IS_ABSOLUTE_PATH(path, path_length)) {
931        /* pass */
932    } else if (UNEXPECTED(is_stream_path(path))) {
933        if (!is_cacheable_stream_path(path)) {
934            return NULL;
935        }
936        /* pass */
937    } else if (UNEXPECTED(!ZCG(accel_directives).use_cwd)) {
938        /* pass */
939    } else {
940        const char *include_path = NULL, *cwd = NULL;
941        int include_path_len = 0, cwd_len = 0;
942        zend_string *parent_script = NULL;
943        size_t parent_script_len = 0;
944
945        if (EXPECTED(ZCG(cwd_key_len))) {
946            cwd = ZCG(cwd_key);
947            cwd_len = ZCG(cwd_key_len);
948        } else {
949            zend_string *cwd_str = accel_getcwd();
950
951            if (UNEXPECTED(!cwd_str)) {
952                /* we don't handle this well for now. */
953                zend_accel_error(ACCEL_LOG_INFO, "getcwd() failed for '%s' (%d), please try to set opcache.use_cwd to 0 in ini file", path, errno);
954                return NULL;
955            }
956            cwd = cwd_str->val;
957            cwd_len = cwd_str->len;
958#ifndef ZTS
959            if (ZCG(cwd_check)) {
960                ZCG(cwd_check) = 0;
961                if ((ZCG(counted) || ZCSG(accelerator_enabled))) {
962
963                    zend_string *str = accel_find_interned_string(cwd_str);
964                    if (!str) {
965                        SHM_UNPROTECT();
966                        zend_shared_alloc_lock();
967                        str = accel_new_interned_string(zend_string_copy(cwd_str));
968                        if (str == cwd_str) {
969                            str = NULL;
970                        }
971                        zend_shared_alloc_unlock();
972                        SHM_PROTECT();
973                    }
974                    if (str) {
975                        char buf[32];
976                        char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, str->val - ZCSG(interned_strings_start));
977
978                        cwd_len = ZCG(cwd_key_len) = buf + sizeof(buf) - 1 - res;
979                        cwd = ZCG(cwd_key);
980                        memcpy(ZCG(cwd_key), res, cwd_len + 1);
981                    }
982                }
983            }
984#endif
985        }
986
987        if (EXPECTED(ZCG(include_path_key_len))) {
988            include_path = ZCG(include_path_key);
989            include_path_len = ZCG(include_path_key_len);
990        } else if (!ZCG(include_path) || ZCG(include_path)->len == 0) {
991            include_path = "";
992            include_path_len = 0;
993        } else {
994            include_path = ZCG(include_path)->val;
995            include_path_len = ZCG(include_path)->len;
996
997#ifndef ZTS
998            if (ZCG(include_path_check)) {
999                ZCG(include_path_check) = 0;
1000                if ((ZCG(counted) || ZCSG(accelerator_enabled))) {
1001
1002                    zend_string *str = accel_find_interned_string(ZCG(include_path));
1003                    if (!str) {
1004                        SHM_UNPROTECT();
1005                        zend_shared_alloc_lock();
1006                        str = accel_new_interned_string(zend_string_copy(ZCG(include_path)));
1007                        if (str == ZCG(include_path)) {
1008                            str = NULL;
1009                        }
1010                        zend_shared_alloc_unlock();
1011                        SHM_PROTECT();
1012                    }
1013                    if (str) {
1014                        char buf[32];
1015                        char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, str->val - ZCSG(interned_strings_start));
1016
1017                        include_path_len = ZCG(include_path_key_len) = buf + sizeof(buf) - 1 - res;
1018                        include_path = ZCG(include_path_key);
1019                        memcpy(ZCG(include_path_key), res, include_path_len + 1);
1020                    }
1021                }
1022            }
1023#endif
1024        }
1025
1026        /* Calculate key length */
1027        if (UNEXPECTED((size_t)(cwd_len + path_length + include_path_len + 2) >= sizeof(ZCG(key)))) {
1028            return NULL;
1029        }
1030
1031        /* Generate key
1032         * Note - the include_path must be the last element in the key,
1033         * since in itself, it may include colons (which we use to separate
1034         * different components of the key)
1035         */
1036        memcpy(ZCG(key), path, path_length);
1037        ZCG(key)[path_length] = ':';
1038        key_length = path_length + 1;
1039        memcpy(ZCG(key) + key_length, cwd, cwd_len);
1040        key_length += cwd_len;
1041
1042        if (include_path_len) {
1043            ZCG(key)[key_length] = ':';
1044            key_length += 1;
1045            memcpy(ZCG(key) + key_length, include_path, include_path_len);
1046            key_length += include_path_len;
1047        }
1048
1049        /* Here we add to the key the parent script directory,
1050         * since fopen_wrappers from version 4.0.7 use current script's path
1051         * in include path too.
1052         */
1053        if (EXPECTED(EG(current_execute_data)) &&
1054            EXPECTED((parent_script = zend_get_executed_filename_ex()) != NULL)) {
1055
1056            parent_script_len = parent_script->len;
1057            while ((--parent_script_len > 0) && !IS_SLASH(parent_script->val[parent_script_len]));
1058
1059            if (UNEXPECTED((size_t)(key_length + parent_script_len + 1) >= sizeof(ZCG(key)))) {
1060                return NULL;
1061            }
1062            ZCG(key)[key_length] = ':';
1063            key_length += 1;
1064            memcpy(ZCG(key) + key_length, parent_script->val, parent_script_len);
1065            key_length += parent_script_len;
1066        }
1067        ZCG(key)[key_length] = '\0';
1068        *key_len = ZCG(key_len) = key_length;
1069        return ZCG(key);
1070    }
1071
1072    /* not use_cwd */
1073    *key_len = path_length;
1074    return (char*)path;
1075}
1076
1077int zend_accel_invalidate(const char *filename, int filename_len, zend_bool force)
1078{
1079    zend_string *realpath;
1080    zend_persistent_script *persistent_script;
1081
1082    if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock() != SUCCESS) {
1083        return FAILURE;
1084    }
1085
1086    realpath = accelerator_orig_zend_resolve_path(filename, filename_len);
1087
1088    if (!realpath) {
1089        return FAILURE;
1090    }
1091
1092    persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath);
1093    if (persistent_script && !persistent_script->corrupted) {
1094        zend_file_handle file_handle;
1095
1096        file_handle.type = ZEND_HANDLE_FILENAME;
1097        file_handle.filename = realpath->val;
1098        file_handle.opened_path = realpath;
1099
1100        if (force ||
1101            !ZCG(accel_directives).validate_timestamps ||
1102            do_validate_timestamps(persistent_script, &file_handle) == FAILURE) {
1103            SHM_UNPROTECT();
1104            zend_shared_alloc_lock();
1105            if (!persistent_script->corrupted) {
1106                persistent_script->corrupted = 1;
1107                persistent_script->timestamp = 0;
1108                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1109                if (ZSMMG(memory_exhausted)) {
1110                    zend_accel_restart_reason reason =
1111                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1112                    zend_accel_schedule_restart_if_necessary(reason);
1113                }
1114            }
1115            zend_shared_alloc_unlock();
1116            SHM_PROTECT();
1117        }
1118    }
1119
1120    accelerator_shm_read_unlock();
1121    efree(realpath);
1122
1123    return SUCCESS;
1124}
1125
1126/* Adds another key for existing cached script */
1127static void zend_accel_add_key(char *key, unsigned int key_length, zend_accel_hash_entry *bucket)
1128{
1129    if (!zend_accel_hash_str_find(&ZCSG(hash), key, key_length)) {
1130        if (zend_accel_hash_is_full(&ZCSG(hash))) {
1131            zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1132            ZSMMG(memory_exhausted) = 1;
1133            zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1134        } else {
1135            char *new_key = zend_shared_alloc(key_length + 1);
1136            if (new_key) {
1137                memcpy(new_key, key, key_length + 1);
1138                if (zend_accel_hash_update(&ZCSG(hash), new_key, key_length, 1, bucket)) {
1139                    zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", new_key);
1140                }
1141            } else {
1142                zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1143            }
1144        }
1145    }
1146}
1147
1148static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length, int *from_shared_memory)
1149{
1150    zend_accel_hash_entry *bucket;
1151    uint memory_used;
1152
1153    /* Check if script may be stored in shared memory */
1154    if (!zend_accel_script_persistable(new_persistent_script)) {
1155        return new_persistent_script;
1156    }
1157
1158    if (!zend_accel_script_optimize(new_persistent_script)) {
1159        return new_persistent_script;
1160    }
1161
1162    /* exclusive lock */
1163    zend_shared_alloc_lock();
1164
1165    if (zend_accel_hash_is_full(&ZCSG(hash))) {
1166        zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1167        ZSMMG(memory_exhausted) = 1;
1168        zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1169        zend_shared_alloc_unlock();
1170        return new_persistent_script;
1171    }
1172
1173    /* Check if we still need to put the file into the cache (may be it was
1174     * already stored by another process. This final check is done under
1175     * exclusive lock) */
1176    bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->full_path);
1177    if (bucket) {
1178        zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;
1179
1180        if (!existing_persistent_script->corrupted) {
1181            if (key &&
1182                (!ZCG(accel_directives).validate_timestamps ||
1183                 (new_persistent_script->timestamp == existing_persistent_script->timestamp))) {
1184                zend_accel_add_key(key, key_length, bucket);
1185            }
1186            zend_shared_alloc_unlock();
1187            return new_persistent_script;
1188        }
1189    }
1190
1191    /* Calculate the required memory size */
1192    memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length);
1193
1194    /* Allocate shared memory */
1195#ifdef __SSE2__
1196    /* Align to 64-byte boundary */
1197    ZCG(mem) = zend_shared_alloc(memory_used + 64);
1198    ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
1199#else
1200    ZCG(mem) = zend_shared_alloc(memory_used);
1201#endif
1202    if (!ZCG(mem)) {
1203        zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1204        zend_shared_alloc_unlock();
1205        return new_persistent_script;
1206    }
1207
1208    /* Copy into shared memory */
1209    new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length);
1210
1211    new_persistent_script->is_phar =
1212        new_persistent_script->full_path &&
1213        strstr(new_persistent_script->full_path->val, ".phar") &&
1214        !strstr(new_persistent_script->full_path->val, "://");
1215
1216    /* Consistency check */
1217    if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1218        zend_accel_error(
1219            ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1220            "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
1221            new_persistent_script->full_path->val,
1222            new_persistent_script->mem,
1223            (char *)new_persistent_script->mem + new_persistent_script->size,
1224            ZCG(mem));
1225    }
1226
1227    new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
1228
1229    /* store script structure in the hash table */
1230    bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->full_path->val, new_persistent_script->full_path->len, 0, new_persistent_script);
1231    if (bucket) {
1232        zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", new_persistent_script->full_path);
1233        if (key &&
1234            /* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
1235            memcmp(key, "phar://", sizeof("phar://") - 1) != 0 &&
1236            (new_persistent_script->full_path->len != key_length ||
1237             memcmp(new_persistent_script->full_path->val, key, key_length) != 0)) {
1238            /* link key to the same persistent script in hash table */
1239            if (zend_accel_hash_update(&ZCSG(hash), key, key_length, 1, bucket)) {
1240                zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", key);
1241            } else {
1242                zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1243                ZSMMG(memory_exhausted) = 1;
1244                zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1245            }
1246        }
1247    }
1248
1249    new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
1250
1251    zend_shared_alloc_unlock();
1252
1253    *from_shared_memory = 1;
1254    return new_persistent_script;
1255}
1256
1257static const struct jit_auto_global_info
1258{
1259    const char *name;
1260    size_t len;
1261} jit_auto_globals_info[] = {
1262    { "_SERVER",  sizeof("_SERVER")-1},
1263    { "_ENV",     sizeof("_ENV")-1},
1264    { "_REQUEST", sizeof("_REQUEST")-1},
1265    { "GLOBALS",  sizeof("GLOBALS")-1},
1266};
1267
1268static zend_string *jit_auto_globals_str[4];
1269
1270static int zend_accel_get_auto_globals(void)
1271{
1272    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1273    int n = 1;
1274    int mask = 0;
1275
1276    for (i = 0; i < ag_size ; i++) {
1277        if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_str[i])) {
1278            mask |= n;
1279        }
1280        n += n;
1281    }
1282    return mask;
1283}
1284
1285static int zend_accel_get_auto_globals_no_jit(void)
1286{
1287    if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_str[3])) {
1288        return 8;
1289    }
1290    return 0;
1291}
1292
1293static void zend_accel_set_auto_globals(int mask)
1294{
1295    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1296    int n = 1;
1297
1298    for (i = 0; i < ag_size ; i++) {
1299        if ((mask & n) && !(ZCG(auto_globals_mask) & n)) {
1300            ZCG(auto_globals_mask) |= n;
1301            zend_is_auto_global(jit_auto_globals_str[i]);
1302        }
1303        n += n;
1304    }
1305}
1306
1307static void zend_accel_init_auto_globals(void)
1308{
1309    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1310
1311    for (i = 0; i < ag_size ; i++) {
1312        jit_auto_globals_str[i] = zend_string_init(jit_auto_globals_info[i].name, jit_auto_globals_info[i].len, 1);
1313        zend_string_hash_val(jit_auto_globals_str[i]);
1314        jit_auto_globals_str[i] = accel_new_interned_string(jit_auto_globals_str[i]);
1315    }
1316}
1317
1318static zend_persistent_script *compile_and_cache_file(zend_file_handle *file_handle, int type, char *key, unsigned int key_length, zend_op_array **op_array_p, int *from_shared_memory)
1319{
1320    zend_persistent_script *new_persistent_script;
1321    zend_op_array *orig_active_op_array;
1322    HashTable *orig_function_table, *orig_class_table;
1323    zval orig_user_error_handler;
1324    zend_op_array *op_array;
1325    int do_bailout = 0;
1326    accel_time_t timestamp = 0;
1327    uint32_t orig_compiler_options = 0;
1328
1329    /* Try to open file */
1330    if (file_handle->type == ZEND_HANDLE_FILENAME) {
1331        if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == SUCCESS) {
1332            /* key may be changed by zend_stream_open_function() */
1333            if (key == ZCG(key)) {
1334                key_length = ZCG(key_len);
1335            }
1336        } else {
1337            *op_array_p = NULL;
1338            if (type == ZEND_REQUIRE) {
1339                zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1340                zend_bailout();
1341            } else {
1342                zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1343            }
1344            return NULL;
1345        }
1346    }
1347
1348    /* check blacklist right after ensuring that file was opened */
1349    if (file_handle->opened_path && zend_accel_blacklist_is_blacklisted(&accel_blacklist, file_handle->opened_path->val)) {
1350        ZCSG(blacklist_misses)++;
1351        *op_array_p = accelerator_orig_compile_file(file_handle, type);
1352        return NULL;
1353    }
1354
1355    if (ZCG(accel_directives).validate_timestamps ||
1356        ZCG(accel_directives).file_update_protection ||
1357        ZCG(accel_directives).max_file_size > 0) {
1358        size_t size = 0;
1359
1360        /* Obtain the file timestamps, *before* actually compiling them,
1361         * otherwise we have a race-condition.
1362         */
1363        timestamp = zend_get_file_handle_timestamp(file_handle, ZCG(accel_directives).max_file_size > 0 ? &size : NULL);
1364
1365        /* If we can't obtain a timestamp (that means file is possibly socket)
1366         *  we won't cache it
1367         */
1368        if (timestamp == 0) {
1369            *op_array_p = accelerator_orig_compile_file(file_handle, type);
1370            return NULL;
1371        }
1372
1373        /* check if file is too new (may be it's not written completely yet) */
1374        if (ZCG(accel_directives).file_update_protection &&
1375            (ZCG(request_time) - ZCG(accel_directives).file_update_protection < timestamp)) {
1376            *op_array_p = accelerator_orig_compile_file(file_handle, type);
1377            return NULL;
1378        }
1379
1380        if (ZCG(accel_directives).max_file_size > 0 && size > (size_t)ZCG(accel_directives).max_file_size) {
1381            ZCSG(blacklist_misses)++;
1382            *op_array_p = accelerator_orig_compile_file(file_handle, type);
1383            return NULL;
1384        }
1385    }
1386
1387    new_persistent_script = create_persistent_script();
1388
1389    /* Save the original values for the op_array, function table and class table */
1390    orig_active_op_array = CG(active_op_array);
1391    orig_function_table = CG(function_table);
1392    orig_class_table = CG(class_table);
1393    ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler));
1394
1395    /* Override them with ours */
1396    CG(function_table) = &ZCG(function_table);
1397    EG(class_table) = CG(class_table) = &new_persistent_script->class_table;
1398    ZVAL_UNDEF(&EG(user_error_handler));
1399
1400    zend_try {
1401        orig_compiler_options = CG(compiler_options);
1402        CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
1403        CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
1404        CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
1405        CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
1406        op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type);
1407        CG(compiler_options) = orig_compiler_options;
1408    } zend_catch {
1409        op_array = NULL;
1410        do_bailout = 1;
1411        CG(compiler_options) = orig_compiler_options;
1412    } zend_end_try();
1413
1414    /* Restore originals */
1415    CG(active_op_array) = orig_active_op_array;
1416    CG(function_table) = orig_function_table;
1417    EG(class_table) = CG(class_table) = orig_class_table;
1418    EG(user_error_handler) = orig_user_error_handler;
1419
1420    if (!op_array) {
1421        /* compilation failed */
1422        free_persistent_script(new_persistent_script, 1);
1423        zend_accel_free_user_functions(&ZCG(function_table));
1424        if (do_bailout) {
1425            zend_bailout();
1426        }
1427        return NULL;
1428    }
1429
1430    /* Build the persistent_script structure.
1431       Here we aren't sure we would store it, but we will need it
1432       further anyway.
1433    */
1434    zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->function_table);
1435    new_persistent_script->main_op_array = *op_array;
1436
1437    efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
1438
1439    /* Fill in the ping_auto_globals_mask for the new script. If jit for auto globals is enabled we
1440       will have to ping the used auto global variables before execution */
1441    if (PG(auto_globals_jit)) {
1442        new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals();
1443    } else {
1444        new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit();
1445    }
1446
1447    if (ZCG(accel_directives).validate_timestamps) {
1448        /* Obtain the file timestamps, *before* actually compiling them,
1449         * otherwise we have a race-condition.
1450         */
1451        new_persistent_script->timestamp = timestamp;
1452        new_persistent_script->dynamic_members.revalidate = ZCG(request_time) + ZCG(accel_directives).revalidate_freq;
1453    }
1454
1455    if (file_handle->opened_path) {
1456        new_persistent_script->full_path = zend_string_copy(file_handle->opened_path);
1457    } else {
1458        new_persistent_script->full_path = zend_string_init(file_handle->filename, strlen(file_handle->filename), 0);
1459    }
1460    zend_string_hash_val(new_persistent_script->full_path);
1461
1462    /* Now persistent_script structure is ready in process memory */
1463    return cache_script_in_shared_memory(new_persistent_script, key, key_length, from_shared_memory);
1464}
1465
1466/* zend_compile() replacement */
1467zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
1468{
1469    zend_persistent_script *persistent_script = NULL;
1470    char *key = NULL;
1471    int key_length;
1472    int from_shared_memory; /* if the script we've got is stored in SHM */
1473
1474    if (!file_handle->filename || !ZCG(enabled) || !accel_startup_ok ||
1475        (!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
1476        (ZCSG(restart_in_progress) && accel_restart_is_active())) {
1477        /* The Accelerator is disabled, act as if without the Accelerator */
1478        return accelerator_orig_compile_file(file_handle, type);
1479    }
1480
1481    /* In case this callback is called from include_once, require_once or it's
1482     * a main FastCGI request, the key must be already calculated, and cached
1483     * persistent script already found */
1484    if (ZCG(cache_persistent_script) &&
1485        ((!EG(current_execute_data) &&
1486          file_handle->filename == SG(request_info).path_translated &&
1487          ZCG(cache_opline) == NULL) ||
1488         (EG(current_execute_data) &&
1489          EG(current_execute_data)->func &&
1490          ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1491          ZCG(cache_opline) == EG(current_execute_data)->opline))) {
1492
1493        persistent_script = ZCG(cache_persistent_script);
1494        if (ZCG(key_len)) {
1495            key = ZCG(key);
1496            key_length = ZCG(key_len);
1497        }
1498
1499    } else {
1500        if (!ZCG(accel_directives).revalidate_path) {
1501            /* try to find cached script by key */
1502            key = accel_make_persistent_key(file_handle->filename, strlen(file_handle->filename), &key_length);
1503            if (!key) {
1504                return accelerator_orig_compile_file(file_handle, type);
1505            }
1506            persistent_script = zend_accel_hash_str_find(&ZCSG(hash), key, key_length);
1507        }
1508        if (!persistent_script) {
1509            /* try to find cached script by full real path */
1510            zend_accel_hash_entry *bucket;
1511
1512            /* open file to resolve the path */
1513            if (file_handle->type == ZEND_HANDLE_FILENAME &&
1514                accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) {
1515                if (type == ZEND_REQUIRE) {
1516                    zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1517                    zend_bailout();
1518                } else {
1519                    zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1520                }
1521                return NULL;
1522            }
1523
1524            if (file_handle->opened_path) {
1525                bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path);
1526
1527                if (bucket) {
1528                    persistent_script = (zend_persistent_script *)bucket->data;
1529
1530                    if (key && !persistent_script->corrupted) {
1531                        SHM_UNPROTECT();
1532                        zend_shared_alloc_lock();
1533                        zend_accel_add_key(key, key_length, bucket);
1534                        zend_shared_alloc_unlock();
1535                        SHM_PROTECT();
1536                    }
1537                }
1538            }
1539        }
1540    }
1541
1542    /* clear cache */
1543    ZCG(cache_opline) = NULL;
1544    ZCG(cache_persistent_script) = NULL;
1545
1546    if (persistent_script && persistent_script->corrupted) {
1547        persistent_script = NULL;
1548    }
1549
1550    /* Make sure we only increase the currently running processes semaphore
1551     * once each execution (this function can be called more than once on
1552     * each execution)
1553     */
1554    if (!ZCG(counted)) {
1555        ZCG(counted) = 1;
1556        accel_activate_add();
1557    }
1558
1559    SHM_UNPROTECT();
1560
1561    /* If script is found then validate_timestamps if option is enabled */
1562    if (persistent_script && ZCG(accel_directives).validate_timestamps) {
1563        if (validate_timestamp_and_record(persistent_script, file_handle) == FAILURE) {
1564            zend_shared_alloc_lock();
1565            if (!persistent_script->corrupted) {
1566                persistent_script->corrupted = 1;
1567                persistent_script->timestamp = 0;
1568                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1569                if (ZSMMG(memory_exhausted)) {
1570                    zend_accel_restart_reason reason =
1571                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1572                    zend_accel_schedule_restart_if_necessary(reason);
1573                }
1574            }
1575            zend_shared_alloc_unlock();
1576            persistent_script = NULL;
1577        }
1578    }
1579
1580    /* if turned on - check the compiled script ADLER32 checksum */
1581    if (persistent_script && ZCG(accel_directives).consistency_checks
1582        && persistent_script->dynamic_members.hits % ZCG(accel_directives).consistency_checks == 0) {
1583
1584        unsigned int checksum = zend_accel_script_checksum(persistent_script);
1585        if (checksum != persistent_script->dynamic_members.checksum ) {
1586            /* The checksum is wrong */
1587            zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s':  expected=0x%0.8X, found=0x%0.8X",
1588                             persistent_script->full_path, persistent_script->dynamic_members.checksum, checksum);
1589            zend_shared_alloc_lock();
1590            if (!persistent_script->corrupted) {
1591                persistent_script->corrupted = 1;
1592                persistent_script->timestamp = 0;
1593                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1594                if (ZSMMG(memory_exhausted)) {
1595                    zend_accel_restart_reason reason =
1596                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1597                    zend_accel_schedule_restart_if_necessary(reason);
1598                }
1599            }
1600            zend_shared_alloc_unlock();
1601            persistent_script = NULL;
1602        }
1603    }
1604
1605    /* If script was not found or invalidated by validate_timestamps */
1606    if (!persistent_script) {
1607        uint32_t old_const_num = zend_hash_next_free_element(EG(zend_constants));
1608        zend_op_array *op_array;
1609
1610        /* Cache miss.. */
1611        ZCSG(misses)++;
1612
1613        /* No memory left. Behave like without the Accelerator */
1614        if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) {
1615            SHM_PROTECT();
1616            return accelerator_orig_compile_file(file_handle, type);
1617        }
1618
1619        /* Try and cache the script and assume that it is returned from_shared_memory.
1620         * If it isn't compile_and_cache_file() changes the flag to 0
1621         */
1622        from_shared_memory = 0;
1623        persistent_script = compile_and_cache_file(file_handle, type, key, key_length, &op_array, &from_shared_memory);
1624
1625        /* Caching is disabled, returning op_array;
1626         * or something went wrong during compilation, returning NULL
1627         */
1628        if (!persistent_script) {
1629            SHM_PROTECT();
1630            return op_array;
1631        }
1632        if (from_shared_memory) {
1633            /* Delete immutable arrays moved into SHM */
1634            uint32_t new_const_num = zend_hash_next_free_element(EG(zend_constants));
1635            while (new_const_num > old_const_num) {
1636                new_const_num--;
1637                zend_hash_index_del(EG(zend_constants), new_const_num);
1638            }
1639        }
1640    } else {
1641
1642#if !ZEND_WIN32
1643        ZCSG(hits)++; /* TBFixed: may lose one hit */
1644        persistent_script->dynamic_members.hits++; /* see above */
1645#else
1646        INCREMENT(hits);
1647        InterlockedIncrement64(&persistent_script->dynamic_members.hits);
1648#endif
1649
1650        /* see bug #15471 (old BTS) */
1651        if (persistent_script->full_path) {
1652            if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
1653                !EG(current_execute_data)->func ||
1654                !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
1655                EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
1656                (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
1657                 EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
1658                if (zend_hash_add_empty_element(&EG(included_files), persistent_script->full_path) != NULL) {
1659                    /* ext/phar has to load phar's metadata into memory */
1660                    if (persistent_script->is_phar) {
1661                        php_stream_statbuf ssb;
1662                        char *fname = emalloc(sizeof("phar://") + persistent_script->full_path->len);
1663
1664                        memcpy(fname, "phar://", sizeof("phar://") - 1);
1665                        memcpy(fname + sizeof("phar://") - 1, persistent_script->full_path->val, persistent_script->full_path->len + 1);
1666                        php_stream_stat_path(fname, &ssb);
1667                        efree(fname);
1668                    }
1669                }
1670            }
1671        }
1672        zend_file_handle_dtor(file_handle);
1673        from_shared_memory = 1;
1674    }
1675
1676    persistent_script->dynamic_members.last_used = ZCG(request_time);
1677
1678    SHM_PROTECT();
1679
1680    /* Fetch jit auto globals used in the script before execution */
1681    if (persistent_script->ping_auto_globals_mask) {
1682        zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
1683    }
1684
1685    return zend_accel_load_script(persistent_script, from_shared_memory);
1686}
1687
1688/* zend_stream_open_function() replacement for PHP 5.3 and above */
1689static int persistent_stream_open_function(const char *filename, zend_file_handle *handle)
1690{
1691    if (ZCG(cache_persistent_script)) {
1692        /* check if callback is called from include_once or it's a main request */
1693        if ((!EG(current_execute_data) &&
1694             filename == SG(request_info).path_translated &&
1695             ZCG(cache_opline) == NULL) ||
1696            (EG(current_execute_data) &&
1697             EG(current_execute_data)->func &&
1698             ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1699             ZCG(cache_opline) == EG(current_execute_data)->opline)) {
1700
1701            /* we are in include_once or FastCGI request */
1702            handle->filename = (char*)filename;
1703            handle->free_filename = 0;
1704            handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->full_path);
1705            handle->type = ZEND_HANDLE_FILENAME;
1706            return SUCCESS;
1707        }
1708        ZCG(cache_opline) = NULL;
1709        ZCG(cache_persistent_script) = NULL;
1710    }
1711    return accelerator_orig_zend_stream_open_function(filename, handle);
1712}
1713
1714/* zend_resolve_path() replacement for PHP 5.3 and above */
1715static zend_string* persistent_zend_resolve_path(const char *filename, int filename_len)
1716{
1717    if (ZCG(enabled) && accel_startup_ok &&
1718        (ZCG(counted) || ZCSG(accelerator_enabled)) &&
1719        !ZCSG(restart_in_progress)) {
1720
1721        /* check if callback is called from include_once or it's a main request */
1722        if ((!EG(current_execute_data) &&
1723             filename == SG(request_info).path_translated) ||
1724            (EG(current_execute_data) &&
1725             EG(current_execute_data)->func &&
1726             ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1727             EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL &&
1728             (EG(current_execute_data)->opline->extended_value == ZEND_INCLUDE_ONCE ||
1729              EG(current_execute_data)->opline->extended_value == ZEND_REQUIRE_ONCE))) {
1730
1731            /* we are in include_once or FastCGI request */
1732            zend_string *resolved_path;
1733            int key_length;
1734            char *key = NULL;
1735
1736            if (!ZCG(accel_directives).revalidate_path) {
1737                /* lookup by "not-real" path */
1738                key = accel_make_persistent_key(filename, filename_len, &key_length);
1739                if (key) {
1740                    zend_accel_hash_entry *bucket = zend_accel_hash_str_find_entry(&ZCSG(hash), key, key_length);
1741                    if (bucket != NULL) {
1742                        zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
1743                        if (!persistent_script->corrupted) {
1744                            ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
1745                            ZCG(cache_persistent_script) = persistent_script;
1746                            return zend_string_copy(persistent_script->full_path);
1747                        }
1748                    }
1749                } else {
1750                    ZCG(cache_opline) = NULL;
1751                    ZCG(cache_persistent_script) = NULL;
1752                    return accelerator_orig_zend_resolve_path(filename, filename_len);
1753                }
1754            }
1755
1756            /* find the full real path */
1757            resolved_path = accelerator_orig_zend_resolve_path(filename, filename_len);
1758
1759            if (resolved_path) {
1760                /* lookup by real path */
1761                zend_accel_hash_entry *bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path);
1762                if (bucket) {
1763                    zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
1764                    if (!persistent_script->corrupted) {
1765                        if (key) {
1766                            /* add another "key" for the same bucket */
1767                            SHM_UNPROTECT();
1768                            zend_shared_alloc_lock();
1769                            zend_accel_add_key(key, key_length, bucket);
1770                            zend_shared_alloc_unlock();
1771                            SHM_PROTECT();
1772                        } else {
1773                            ZCG(key_len) = 0;
1774                        }
1775                        ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
1776                        ZCG(cache_persistent_script) = persistent_script;
1777                        return resolved_path;
1778                    }
1779                }
1780            }
1781
1782            ZCG(cache_opline) = NULL;
1783            ZCG(cache_persistent_script) = NULL;
1784            return resolved_path;
1785        }
1786    }
1787    ZCG(cache_opline) = NULL;
1788    ZCG(cache_persistent_script) = NULL;
1789    return accelerator_orig_zend_resolve_path(filename, filename_len);
1790}
1791
1792static void zend_reset_cache_vars(void)
1793{
1794    ZSMMG(memory_exhausted) = 0;
1795    ZCSG(hits) = 0;
1796    ZCSG(misses) = 0;
1797    ZCSG(blacklist_misses) = 0;
1798    ZSMMG(wasted_shared_memory) = 0;
1799    ZCSG(restart_pending) = 0;
1800    ZCSG(force_restart_time) = 0;
1801}
1802
1803#ifndef ZTS
1804static void accel_reset_pcre_cache(void)
1805{
1806    Bucket *p;
1807
1808    ZEND_HASH_FOREACH_BUCKET(&PCRE_G(pcre_cache), p) {
1809        /* Remove PCRE cache entries with inconsistent keys */
1810        if (IS_ACCEL_INTERNED(p->key)) {
1811            p->key = NULL;
1812            zend_hash_del_bucket(&PCRE_G(pcre_cache), p);
1813        }
1814    } ZEND_HASH_FOREACH_END();
1815}
1816#endif
1817
1818static void accel_activate(void)
1819{
1820#ifndef ZTS
1821    zend_bool reset_pcre = 0;
1822#endif
1823
1824    if (!ZCG(enabled) || !accel_startup_ok) {
1825        return;
1826    }
1827
1828    if (!ZCG(function_table).nTableSize) {
1829        zend_hash_init(&ZCG(function_table), zend_hash_num_elements(CG(function_table)), NULL, ZEND_FUNCTION_DTOR, 1);
1830        zend_accel_copy_internal_functions();
1831    }
1832
1833    SHM_UNPROTECT();
1834    /* PHP-5.4 and above return "double", but we use 1 sec precision */
1835    ZCG(auto_globals_mask) = 0;
1836    ZCG(request_time) = (time_t)sapi_get_request_time();
1837    ZCG(cache_opline) = NULL;
1838    ZCG(cache_persistent_script) = NULL;
1839    ZCG(include_path_key_len) = 0;
1840    ZCG(include_path_check) = 1;
1841
1842    if (ZCG(counted)) {
1843#ifdef ZTS
1844        zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %d", tsrm_thread_id());
1845#else
1846        zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid());
1847#endif
1848        accel_unlock_all();
1849        ZCG(counted) = 0;
1850    }
1851
1852    if (ZCSG(restart_pending)) {
1853        zend_shared_alloc_lock();
1854        if (ZCSG(restart_pending) != 0) { /* check again, to ensure that the cache wasn't already cleaned by another process */
1855            if (accel_is_inactive() == SUCCESS) {
1856                zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!");
1857                ZCSG(restart_pending) = 0;
1858                switch ZCSG(restart_reason) {
1859                    case ACCEL_RESTART_OOM:
1860                        ZCSG(oom_restarts)++;
1861                        break;
1862                    case ACCEL_RESTART_HASH:
1863                        ZCSG(hash_restarts)++;
1864                        break;
1865                    case ACCEL_RESTART_USER:
1866                        ZCSG(manual_restarts)++;
1867                        break;
1868                }
1869                accel_restart_enter();
1870
1871                zend_reset_cache_vars();
1872                zend_accel_hash_clean(&ZCSG(hash));
1873
1874#if !defined(ZTS)
1875                if (ZCG(accel_directives).interned_strings_buffer) {
1876                    accel_interned_strings_restore_state();
1877                }
1878#endif
1879
1880                zend_shared_alloc_restore_state();
1881                ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart);
1882                if (ZCSG(last_restart_time) < ZCG(request_time)) {
1883                    ZCSG(last_restart_time) = ZCG(request_time);
1884                } else {
1885                    ZCSG(last_restart_time)++;
1886                }
1887                accel_restart_leave();
1888            }
1889#ifndef ZTS
1890        } else {
1891            reset_pcre = 1;
1892#endif
1893        }
1894        zend_shared_alloc_unlock();
1895    }
1896
1897    /* check if ZCG(function_table) wasn't somehow polluted on the way */
1898    if (ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) {
1899        zend_accel_error(ACCEL_LOG_WARNING, "Internal functions count changed - was %d, now %d", ZCG(internal_functions_count), zend_hash_num_elements(&ZCG(function_table)));
1900    }
1901
1902    ZCG(cwd) = NULL;
1903    ZCG(cwd_key_len) = 0;
1904    ZCG(cwd_check) = 1;
1905
1906    SHM_PROTECT();
1907
1908    if (ZCSG(last_restart_time) != ZCG(last_restart_time)) {
1909        /* SHM was reinitialized. */
1910        ZCG(last_restart_time) = ZCSG(last_restart_time);
1911
1912        /* Reset in-process realpath cache */
1913        realpath_cache_clean();
1914
1915#ifndef ZTS
1916        accel_reset_pcre_cache();
1917    } else if (reset_pcre) {
1918        accel_reset_pcre_cache();
1919#endif
1920    }
1921}
1922
1923#if !ZEND_DEBUG
1924
1925/* Fast Request Shutdown
1926 * =====================
1927 * Zend Memory Manager frees memory by its own. We don't have to free each
1928 * allocated block separately, but we like to call all the destructors and
1929 * callbacks in exactly the same order.
1930 */
1931static void accel_fast_hash_destroy(HashTable *ht);
1932
1933static void accel_fast_zval_dtor(zval *zvalue)
1934{
1935tail_call:
1936    switch (Z_TYPE_P(zvalue)) {
1937        case IS_ARRAY:
1938            GC_REMOVE_FROM_BUFFER(Z_ARR_P(zvalue));
1939            if (Z_ARR_P(zvalue) != &EG(symbol_table)) {
1940                /* break possible cycles */
1941                ZVAL_NULL(zvalue);
1942                accel_fast_hash_destroy(Z_ARRVAL_P(zvalue));
1943            }
1944            break;
1945        case IS_OBJECT:
1946            OBJ_RELEASE(Z_OBJ_P(zvalue));
1947            break;
1948        case IS_RESOURCE:
1949            zend_list_delete(Z_RES_P(zvalue));
1950            break;
1951        case IS_REFERENCE: {
1952                zend_reference *ref = Z_REF_P(zvalue);
1953
1954                if (--GC_REFCOUNT(ref) == 0) {
1955                    if (Z_REFCOUNTED(ref->val) && Z_DELREF(ref->val) == 0) {
1956                        zvalue = &ref->val;
1957                        goto tail_call;
1958                    }
1959                }
1960            }
1961            break;
1962    }
1963}
1964
1965static void accel_fast_hash_destroy(HashTable *ht)
1966{
1967    Bucket *p = ht->arData;
1968    Bucket *end = p + ht->nNumUsed;
1969
1970    while (p != end) {
1971        if (Z_REFCOUNTED(p->val) && Z_DELREF(p->val) == 0) {
1972            accel_fast_zval_dtor(&p->val);
1973        }
1974        p++;
1975    }
1976}
1977
1978static inline void zend_accel_fast_del_bucket(HashTable *ht, uint32_t idx, Bucket *p)
1979{
1980    uint32_t nIndex = p->h | ht->nTableMask;
1981    uint32_t i = HT_HASH(ht, nIndex);
1982
1983    ht->nNumUsed--;
1984    ht->nNumOfElements--;
1985    if (idx != i) {
1986        Bucket *prev = HT_HASH_TO_BUCKET(ht, i);
1987        while (Z_NEXT(prev->val) != idx) {
1988            i = Z_NEXT(prev->val);
1989            prev = HT_HASH_TO_BUCKET(ht, i);
1990        }
1991        Z_NEXT(prev->val) = Z_NEXT(p->val);
1992    } else {
1993        HT_HASH(ht, p->h | ht->nTableMask) = Z_NEXT(p->val);
1994    }
1995}
1996
1997static void zend_accel_fast_shutdown(void)
1998{
1999    if (EG(full_tables_cleanup)) {
2000        return;
2001    }
2002
2003    if (EG(objects_store).top > 1 || zend_hash_num_elements(&EG(regular_list)) > 0) {
2004        /* We don't have to destroy all zvals if they cannot call any destructors */
2005        zend_try {
2006            ZEND_HASH_REVERSE_FOREACH(&EG(symbol_table), 0) {
2007                if (Z_REFCOUNTED(_p->val) && Z_DELREF(_p->val) == 0) {
2008                    accel_fast_zval_dtor(&_p->val);
2009                }
2010                zend_accel_fast_del_bucket(&EG(symbol_table), HT_IDX_TO_HASH(_idx-1), _p);
2011            } ZEND_HASH_FOREACH_END();
2012        } zend_end_try();
2013        zend_hash_init(&EG(symbol_table), 8, NULL, NULL, 0);
2014
2015        ZEND_HASH_REVERSE_FOREACH(EG(function_table), 0) {
2016            zend_function *func = Z_PTR(_p->val);
2017
2018            if (func->type == ZEND_INTERNAL_FUNCTION) {
2019                break;
2020            } else {
2021                if (func->op_array.static_variables) {
2022                    if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
2023                        if (--GC_REFCOUNT(func->op_array.static_variables) == 0) {
2024                            accel_fast_hash_destroy(func->op_array.static_variables);
2025                        }
2026                    }
2027                }
2028                zend_accel_fast_del_bucket(EG(function_table), HT_IDX_TO_HASH(_idx-1), _p);
2029            }
2030        } ZEND_HASH_FOREACH_END();
2031
2032        ZEND_HASH_REVERSE_FOREACH(EG(class_table), 0) {
2033            zend_class_entry *ce = Z_PTR(_p->val);
2034
2035            if (ce->type == ZEND_INTERNAL_CLASS) {
2036                break;
2037            } else {
2038                if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
2039                    zend_function *func;
2040
2041                    ZEND_HASH_FOREACH_PTR(&ce->function_table, func) {
2042                        if (func->type == ZEND_USER_FUNCTION) {
2043                            if (func->op_array.static_variables) {
2044                                if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
2045                                    if (--GC_REFCOUNT(func->op_array.static_variables) == 0) {
2046                                        accel_fast_hash_destroy(func->op_array.static_variables);
2047                                    }
2048                                }
2049                                func->op_array.static_variables = NULL;
2050                            }
2051                        }
2052                    } ZEND_HASH_FOREACH_END();
2053                }
2054                if (ce->static_members_table) {
2055                    int i;
2056
2057                    for (i = 0; i < ce->default_static_members_count; i++) {
2058                        zval *zv = &ce->static_members_table[i];
2059                        ZVAL_UNDEF(&ce->static_members_table[i]);
2060                        if (Z_REFCOUNTED_P(zv) && Z_DELREF_P(zv) == 0) {
2061                            accel_fast_zval_dtor(zv);
2062                        }
2063                    }
2064                    ce->static_members_table = NULL;
2065                }
2066                zend_accel_fast_del_bucket(EG(class_table), HT_IDX_TO_HASH(_idx-1), _p);
2067            }
2068        } ZEND_HASH_FOREACH_END();
2069
2070    } else {
2071
2072        zend_hash_init(&EG(symbol_table), 8, NULL, NULL, 0);
2073
2074        ZEND_HASH_REVERSE_FOREACH(EG(function_table), 0) {
2075            zend_function *func = Z_PTR(_p->val);
2076
2077            if (func->type == ZEND_INTERNAL_FUNCTION) {
2078                break;
2079            } else {
2080                zend_accel_fast_del_bucket(EG(function_table), HT_IDX_TO_HASH(_idx-1), _p);
2081            }
2082        } ZEND_HASH_FOREACH_END();
2083
2084        ZEND_HASH_REVERSE_FOREACH(EG(class_table), 0) {
2085            zend_class_entry *ce = Z_PTR(_p->val);
2086
2087            if (ce->type == ZEND_INTERNAL_CLASS) {
2088                break;
2089            } else {
2090                zend_accel_fast_del_bucket(EG(class_table), HT_IDX_TO_HASH(_idx-1), _p);
2091            }
2092        } ZEND_HASH_FOREACH_END();
2093    }
2094
2095    ZEND_HASH_REVERSE_FOREACH(EG(zend_constants), 0) {
2096        zend_constant *c = Z_PTR(_p->val);
2097
2098        if (c->flags & CONST_PERSISTENT) {
2099            break;
2100        } else {
2101            zend_accel_fast_del_bucket(EG(zend_constants), HT_IDX_TO_HASH(_idx-1), _p);
2102        }
2103    } ZEND_HASH_FOREACH_END();
2104
2105    CG(unclean_shutdown) = 1;
2106}
2107#endif
2108
2109static void accel_deactivate(void)
2110{
2111    /* ensure that we restore function_table and class_table
2112     * In general, they're restored by persistent_compile_file(), but in case
2113     * the script is aborted abnormally, they may become messed up.
2114     */
2115
2116    if (!ZCG(enabled) || !accel_startup_ok) {
2117        return;
2118    }
2119
2120    zend_shared_alloc_safe_unlock(); /* be sure we didn't leave cache locked */
2121    accel_unlock_all();
2122    ZCG(counted) = 0;
2123
2124#if !ZEND_DEBUG
2125    if (ZCG(accel_directives).fast_shutdown) {
2126        zend_accel_fast_shutdown();
2127    }
2128#endif
2129
2130    if (ZCG(cwd)) {
2131        zend_string_release(ZCG(cwd));
2132        ZCG(cwd) = NULL;
2133    }
2134
2135}
2136
2137static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2)
2138{
2139    (void)element2; /* keep the compiler happy */
2140
2141    if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) {
2142        element1->startup = NULL;
2143#if 0
2144        /* We have to call shutdown callback it to free TS resources */
2145        element1->shutdown = NULL;
2146#endif
2147        element1->activate = NULL;
2148        element1->deactivate = NULL;
2149        element1->op_array_handler = NULL;
2150
2151#ifdef __DEBUG_MESSAGES__
2152        fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error"));
2153        fflush(stderr);
2154#endif
2155    }
2156
2157    return 0;
2158}
2159
2160static void zps_startup_failure(char *reason, char *api_reason, int (*cb)(zend_extension *, zend_extension *))
2161{
2162    accel_startup_ok = 0;
2163    zps_failure_reason = reason;
2164    zps_api_failure_reason = api_reason?api_reason:reason;
2165    zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb);
2166}
2167
2168static inline int accel_find_sapi(void)
2169{
2170    static const char *supported_sapis[] = {
2171        "apache",
2172        "fastcgi",
2173        "cli-server",
2174        "cgi-fcgi",
2175        "fpm-fcgi",
2176        "isapi",
2177        "apache2filter",
2178        "apache2handler",
2179        "litespeed",
2180        NULL
2181    };
2182    const char **sapi_name;
2183
2184    if (sapi_module.name) {
2185        for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
2186            if (strcmp(sapi_module.name, *sapi_name) == 0) {
2187                return SUCCESS;
2188            }
2189        }
2190        if (ZCG(accel_directives).enable_cli &&
2191            strcmp(sapi_module.name, "cli") == 0) {
2192            return SUCCESS;
2193        }
2194    }
2195
2196    return FAILURE;
2197}
2198
2199static int zend_accel_init_shm(void)
2200{
2201    zend_shared_alloc_lock();
2202
2203    accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals));
2204    if (!accel_shared_globals) {
2205        zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
2206        return FAILURE;
2207    }
2208    ZSMMG(app_shared_globals) = accel_shared_globals;
2209
2210    zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files);
2211
2212    ZCSG(interned_strings_start) = ZCSG(interned_strings_end) = NULL;
2213# ifndef ZTS
2214    zend_hash_init(&ZCSG(interned_strings), (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024) / (sizeof(Bucket) + sizeof(Bucket*) + 8 /* average string length */), NULL, NULL, 1);
2215    if (ZCG(accel_directives).interned_strings_buffer) {
2216        void *data;
2217
2218        ZCSG(interned_strings).nTableMask = -ZCSG(interned_strings).nTableSize;
2219        data = zend_shared_alloc(HT_SIZE(&ZCSG(interned_strings)));
2220        ZCSG(interned_strings_start) = zend_shared_alloc((ZCG(accel_directives).interned_strings_buffer * 1024 * 1024));
2221        if (!data || !ZCSG(interned_strings_start)) {
2222            zend_accel_error(ACCEL_LOG_FATAL, ACCELERATOR_PRODUCT_NAME " cannot allocate buffer for interned strings");
2223            return FAILURE;
2224        }
2225        HT_SET_DATA_ADDR(&ZCSG(interned_strings), data);
2226        HT_HASH_RESET(&ZCSG(interned_strings));
2227        ZCSG(interned_strings_end)   = ZCSG(interned_strings_start) + (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024);
2228        ZCSG(interned_strings_top)   = ZCSG(interned_strings_start);
2229
2230//      orig_interned_strings_start = CG(interned_strings_start);
2231//      orig_interned_strings_end = CG(interned_strings_end);
2232//      CG(interned_strings_start) = ZCSG(interned_strings_start);
2233//      CG(interned_strings_end) = ZCSG(interned_strings_end);
2234    }
2235# endif
2236
2237    orig_new_interned_string = zend_new_interned_string;
2238    orig_interned_strings_snapshot = zend_interned_strings_snapshot;
2239    orig_interned_strings_restore = zend_interned_strings_restore;
2240    zend_new_interned_string = accel_new_interned_string_for_php;
2241    zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
2242    zend_interned_strings_restore = accel_interned_strings_restore_for_php;
2243
2244# ifndef ZTS
2245    if (ZCG(accel_directives).interned_strings_buffer) {
2246        accel_use_shm_interned_strings();
2247        accel_interned_strings_save_state();
2248    }
2249# endif
2250
2251    zend_reset_cache_vars();
2252
2253    ZCSG(oom_restarts) = 0;
2254    ZCSG(hash_restarts) = 0;
2255    ZCSG(manual_restarts) = 0;
2256
2257    ZCSG(accelerator_enabled) = 1;
2258    ZCSG(start_time) = zend_accel_get_time();
2259    ZCSG(last_restart_time) = 0;
2260    ZCSG(restart_in_progress) = 0;
2261
2262    zend_shared_alloc_unlock();
2263
2264    return SUCCESS;
2265}
2266
2267static void accel_globals_ctor(zend_accel_globals *accel_globals)
2268{
2269#if defined(COMPILE_DL_OPCACHE) && defined(ZTS)
2270    ZEND_TSRMLS_CACHE_UPDATE();
2271#endif
2272    memset(accel_globals, 0, sizeof(zend_accel_globals));
2273}
2274
2275static void accel_globals_internal_func_dtor(zval *zv)
2276{
2277    free(Z_PTR_P(zv));
2278}
2279
2280static void accel_globals_dtor(zend_accel_globals *accel_globals)
2281{
2282    if (accel_globals->function_table.nTableSize) {
2283        accel_globals->function_table.pDestructor = accel_globals_internal_func_dtor;
2284        zend_hash_destroy(&accel_globals->function_table);
2285    }
2286}
2287
2288static int accel_startup(zend_extension *extension)
2289{
2290    zend_function *func;
2291    zend_ini_entry *ini_entry;
2292
2293#ifdef ZTS
2294    accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, (ts_allocate_dtor) accel_globals_dtor);
2295#else
2296    accel_globals_ctor(&accel_globals);
2297#endif
2298
2299#ifdef ZEND_WIN32
2300    _setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */
2301#endif
2302
2303    if (start_accel_module() == FAILURE) {
2304        accel_startup_ok = 0;
2305        zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!");
2306        return FAILURE;
2307    }
2308
2309    /* no supported SAPI found - disable acceleration and stop initialization */
2310    if (accel_find_sapi() == FAILURE) {
2311        accel_startup_ok = 0;
2312        if (!ZCG(accel_directives).enable_cli &&
2313            strcmp(sapi_module.name, "cli") == 0) {
2314            zps_startup_failure("Opcode Caching is disabled for CLI", NULL, accelerator_remove_cb);
2315        } else {
2316            zps_startup_failure("Opcode Caching is only supported in Apache, ISAPI, FPM, FastCGI and LiteSpeed SAPIs", NULL, accelerator_remove_cb);
2317        }
2318        return SUCCESS;
2319    }
2320
2321    if (ZCG(enabled) == 0) {
2322        return SUCCESS ;
2323    }
2324/********************************************/
2325/* End of non-SHM dependent initializations */
2326/********************************************/
2327    switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) {
2328        case ALLOC_SUCCESS:
2329            if (zend_accel_init_shm() == FAILURE) {
2330                accel_startup_ok = 0;
2331                return FAILURE;
2332            }
2333            break;
2334        case ALLOC_FAILURE:
2335            accel_startup_ok = 0;
2336            zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
2337            return SUCCESS;
2338        case SUCCESSFULLY_REATTACHED:
2339            accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
2340            zend_shared_alloc_lock();
2341            orig_new_interned_string = zend_new_interned_string;
2342            orig_interned_strings_snapshot = zend_interned_strings_snapshot;
2343            orig_interned_strings_restore = zend_interned_strings_restore;
2344
2345            zend_new_interned_string = accel_new_interned_string_for_php;
2346            zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
2347            zend_interned_strings_restore = accel_interned_strings_restore_for_php;
2348#ifndef ZTS
2349            accel_use_shm_interned_strings();
2350#endif
2351            zend_shared_alloc_unlock();
2352            break;
2353        case FAILED_REATTACHED:
2354            accel_startup_ok = 0;
2355            zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
2356            return SUCCESS;
2357            break;
2358    }
2359
2360    /* remeber the last restart time in the process memory */
2361    ZCG(last_restart_time) = ZCSG(last_restart_time);
2362
2363    /* from this point further, shared memory is supposed to be OK */
2364
2365    /* Init auto-global strings */
2366    zend_accel_init_auto_globals();
2367
2368    /* Override compiler */
2369    accelerator_orig_compile_file = zend_compile_file;
2370    zend_compile_file = persistent_compile_file;
2371
2372    /* Override stream opener function (to eliminate open() call caused by
2373     * include/require statements ) */
2374    accelerator_orig_zend_stream_open_function = zend_stream_open_function;
2375    zend_stream_open_function = persistent_stream_open_function;
2376
2377    /* Override path resolver function (to eliminate stat() calls caused by
2378     * include_once/require_once statements */
2379    accelerator_orig_zend_resolve_path = zend_resolve_path;
2380    zend_resolve_path = persistent_zend_resolve_path;
2381
2382    /* Override chdir() function */
2383    if ((func = zend_hash_str_find_ptr(CG(function_table), "chdir", sizeof("chdir")-1)) != NULL &&
2384        func->type == ZEND_INTERNAL_FUNCTION) {
2385        orig_chdir = func->internal_function.handler;
2386        func->internal_function.handler = ZEND_FN(accel_chdir);
2387    }
2388    ZCG(cwd) = NULL;
2389    ZCG(include_path) = NULL;
2390
2391    /* Override "include_path" modifier callback */
2392    if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
2393        ZCG(include_path) = ini_entry->value;
2394        orig_include_path_on_modify = ini_entry->on_modify;
2395        ini_entry->on_modify = accel_include_path_on_modify;
2396    }
2397
2398    zend_shared_alloc_lock();
2399    zend_shared_alloc_save_state();
2400    zend_shared_alloc_unlock();
2401
2402    SHM_PROTECT();
2403
2404    accel_startup_ok = 1;
2405
2406    /* Override file_exists(), is_file() and is_readable() */
2407    zend_accel_override_file_functions();
2408
2409    /* Load black list */
2410    accel_blacklist.entries = NULL;
2411    if (ZCG(enabled) && accel_startup_ok &&
2412        ZCG(accel_directives).user_blacklist_filename &&
2413        *ZCG(accel_directives.user_blacklist_filename)) {
2414        zend_accel_blacklist_init(&accel_blacklist);
2415        zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
2416    }
2417
2418    return SUCCESS;
2419}
2420
2421static void accel_free_ts_resources()
2422{
2423#ifndef ZTS
2424    accel_globals_dtor(&accel_globals);
2425#else
2426    ts_free_id(accel_globals_id);
2427#endif
2428}
2429
2430void accel_shutdown(void)
2431{
2432    zend_ini_entry *ini_entry;
2433
2434    zend_accel_blacklist_shutdown(&accel_blacklist);
2435
2436    if (!ZCG(enabled) || !accel_startup_ok) {
2437        accel_free_ts_resources();
2438        return;
2439    }
2440
2441    if (ZCG(accel_directives).interned_strings_buffer) {
2442#ifndef ZTS
2443        zend_hash_clean(CG(auto_globals));
2444        zend_hash_clean(CG(function_table));
2445        zend_hash_clean(CG(class_table));
2446        zend_hash_clean(EG(zend_constants));
2447
2448        accel_reset_pcre_cache();
2449#endif
2450    }
2451
2452    zend_new_interned_string = orig_new_interned_string;
2453    zend_interned_strings_snapshot = orig_interned_strings_snapshot;
2454    zend_interned_strings_restore = orig_interned_strings_restore;
2455
2456    accel_free_ts_resources();
2457    zend_shared_alloc_shutdown();
2458    zend_compile_file = accelerator_orig_compile_file;
2459
2460    if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
2461        ini_entry->on_modify = orig_include_path_on_modify;
2462    }
2463}
2464
2465void zend_accel_schedule_restart(zend_accel_restart_reason reason)
2466{
2467    if (ZCSG(restart_pending)) {
2468        /* don't schedule twice */
2469        return;
2470    }
2471    zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled!");
2472
2473    SHM_UNPROTECT();
2474    ZCSG(restart_pending) = 1;
2475    ZCSG(restart_reason) = reason;
2476    ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled);
2477    ZCSG(accelerator_enabled) = 0;
2478
2479    if (ZCG(accel_directives).force_restart_timeout) {
2480        ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout;
2481    } else {
2482        ZCSG(force_restart_time) = 0;
2483    }
2484    SHM_PROTECT();
2485}
2486
2487/* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
2488#ifdef ZEND_WIN32
2489#define accel_deactivate_now() ZCG(counted) = 1; accel_deactivate_sub()
2490#else
2491#define accel_deactivate_now() accel_deactivate_sub()
2492#endif
2493
2494/* ensures it is OK to read SHM
2495    if it's not OK (restart in progress) returns FAILURE
2496    if OK returns SUCCESS
2497    MUST call accelerator_shm_read_unlock after done lock operations
2498*/
2499int accelerator_shm_read_lock(void)
2500{
2501    if (ZCG(counted)) {
2502        /* counted means we are holding read lock for SHM, so that nothing bad can happen */
2503        return SUCCESS;
2504    } else {
2505        /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled
2506            or is in progress now */
2507        accel_activate_add(); /* acquire usage lock */
2508        /* Now if we weren't inside restart, restart would not begin until we remove usage lock */
2509        if (ZCSG(restart_in_progress)) {
2510            /* we already were inside restart this means it's not safe to touch shm */
2511            accel_deactivate_now(); /* drop usage lock */
2512            return FAILURE;
2513        }
2514    }
2515    return SUCCESS;
2516}
2517
2518/* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */
2519void accelerator_shm_read_unlock(void)
2520{
2521    if (!ZCG(counted)) {
2522        /* counted is 0 - meaning we had to readlock manually, release readlock now */
2523        accel_deactivate_now();
2524    }
2525}
2526
2527ZEND_EXT_API zend_extension zend_extension_entry = {
2528    ACCELERATOR_PRODUCT_NAME,               /* name */
2529    ACCELERATOR_VERSION,                    /* version */
2530    "Zend Technologies",                    /* author */
2531    "http://www.zend.com/",                 /* URL */
2532    "Copyright (c) 1999-2015",              /* copyright */
2533    accel_startup,                          /* startup */
2534    NULL,                                   /* shutdown */
2535    accel_activate,                         /* per-script activation */
2536    accel_deactivate,                       /* per-script deactivation */
2537    NULL,                                   /* message handler */
2538    NULL,                                   /* op_array handler */
2539    NULL,                                   /* extended statement handler */
2540    NULL,                                   /* extended fcall begin handler */
2541    NULL,                                   /* extended fcall end handler */
2542    NULL,                                   /* op_array ctor */
2543    NULL,                                   /* op_array dtor */
2544    STANDARD_ZEND_EXTENSION_PROPERTIES
2545};
2546