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