1/*
2   +----------------------------------------------------------------------+
3   | Zend OPcache                                                         |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1998-2015 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Andi Gutmans <andi@zend.com>                                |
16   |          Zeev Suraski <zeev@zend.com>                                |
17   |          Stanislav Malyshev <stas@zend.com>                          |
18   |          Dmitry Stogov <dmitry@zend.com>                             |
19   +----------------------------------------------------------------------+
20*/
21
22#include "main/php.h"
23#include "main/php_globals.h"
24#include "zend.h"
25#include "zend_extensions.h"
26#include "zend_compile.h"
27#include "ZendAccelerator.h"
28#include "zend_persist.h"
29#include "zend_shared_alloc.h"
30#include "zend_accelerator_module.h"
31#include "zend_accelerator_blacklist.h"
32#include "zend_list.h"
33#include "zend_execute.h"
34#include "main/SAPI.h"
35#include "main/php_streams.h"
36#include "main/php_open_temporary_file.h"
37#include "zend_API.h"
38#include "zend_ini.h"
39#include "zend_virtual_cwd.h"
40#include "zend_accelerator_util_funcs.h"
41#include "zend_accelerator_hash.h"
42
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#ifdef __SSE2__
1167    /* Align to 64-byte boundary */
1168    ZCG(mem) = zend_shared_alloc(memory_used + 64);
1169    ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
1170#else
1171    ZCG(mem) = zend_shared_alloc(memory_used);
1172#endif
1173    if (!ZCG(mem)) {
1174        zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1175        zend_shared_alloc_unlock();
1176        return new_persistent_script;
1177    }
1178
1179    /* Copy into shared memory */
1180    new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length);
1181
1182    new_persistent_script->is_phar =
1183        new_persistent_script->full_path &&
1184        strstr(new_persistent_script->full_path->val, ".phar") &&
1185        !strstr(new_persistent_script->full_path->val, "://");
1186
1187    /* Consistency check */
1188    if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1189        zend_accel_error(
1190            ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1191            "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
1192            new_persistent_script->full_path->val,
1193            new_persistent_script->mem,
1194            (char *)new_persistent_script->mem + new_persistent_script->size,
1195            ZCG(mem));
1196    }
1197
1198    new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
1199
1200    /* store script structure in the hash table */
1201    bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->full_path->val, new_persistent_script->full_path->len, 0, new_persistent_script);
1202    if (bucket) {
1203        zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", new_persistent_script->full_path);
1204        if (!ZCG(accel_directives).revalidate_path &&
1205            /* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
1206            memcmp(key, "phar://", sizeof("phar://") - 1) != 0 &&
1207            (new_persistent_script->full_path->len != key_length ||
1208             memcmp(new_persistent_script->full_path->val, key, key_length) != 0)) {
1209            /* link key to the same persistent script in hash table */
1210            if (zend_accel_hash_update(&ZCSG(hash), key, key_length, 1, bucket)) {
1211                zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", key);
1212            } else {
1213                zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1214                ZSMMG(memory_exhausted) = 1;
1215                zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1216            }
1217        }
1218    }
1219
1220    new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
1221
1222    zend_shared_alloc_unlock();
1223
1224    *from_shared_memory = 1;
1225    return new_persistent_script;
1226}
1227
1228static const struct jit_auto_global_info
1229{
1230    const char *name;
1231    size_t len;
1232} jit_auto_globals_info[] = {
1233    { "_SERVER",  sizeof("_SERVER")-1},
1234    { "_ENV",     sizeof("_ENV")-1},
1235    { "_REQUEST", sizeof("_REQUEST")-1},
1236    { "GLOBALS",  sizeof("GLOBALS")-1},
1237};
1238
1239static zend_string *jit_auto_globals_str[4];
1240
1241static int zend_accel_get_auto_globals(void)
1242{
1243    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1244    int n = 1;
1245    int mask = 0;
1246
1247    for (i = 0; i < ag_size ; i++) {
1248        if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_str[i])) {
1249            mask |= n;
1250        }
1251        n += n;
1252    }
1253    return mask;
1254}
1255
1256static int zend_accel_get_auto_globals_no_jit(void)
1257{
1258    if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_str[3])) {
1259        return 8;
1260    }
1261    return 0;
1262}
1263
1264static void zend_accel_set_auto_globals(int mask)
1265{
1266    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1267    int n = 1;
1268
1269    for (i = 0; i < ag_size ; i++) {
1270        if ((mask & n) && !(ZCG(auto_globals_mask) & n)) {
1271            ZCG(auto_globals_mask) |= n;
1272            zend_is_auto_global(jit_auto_globals_str[i]);
1273        }
1274        n += n;
1275    }
1276}
1277
1278static void zend_accel_init_auto_globals(void)
1279{
1280    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1281
1282    for (i = 0; i < ag_size ; i++) {
1283        jit_auto_globals_str[i] = zend_string_init(jit_auto_globals_info[i].name, jit_auto_globals_info[i].len, 1);
1284        zend_string_hash_val(jit_auto_globals_str[i]);
1285        jit_auto_globals_str[i] = accel_new_interned_string(jit_auto_globals_str[i]);
1286    }
1287}
1288
1289static 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)
1290{
1291    zend_persistent_script *new_persistent_script;
1292    zend_op_array *orig_active_op_array;
1293    HashTable *orig_function_table, *orig_class_table;
1294    zval orig_user_error_handler;
1295    zend_op_array *op_array;
1296    int do_bailout = 0;
1297    accel_time_t timestamp = 0;
1298    uint32_t orig_compiler_options = 0;
1299
1300    /* Try to open file */
1301    if (file_handle->type == ZEND_HANDLE_FILENAME) {
1302        if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == SUCCESS) {
1303            /* key may be changed by zend_stream_open_function() */
1304            if (key == ZCG(key)) {
1305                key_length = ZCG(key_len);
1306            }
1307        } else {
1308            *op_array_p = NULL;
1309            if (type == ZEND_REQUIRE) {
1310                zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1311                zend_bailout();
1312            } else {
1313                zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1314            }
1315            return NULL;
1316        }
1317    }
1318
1319    /* check blacklist right after ensuring that file was opened */
1320    if (file_handle->opened_path && zend_accel_blacklist_is_blacklisted(&accel_blacklist, file_handle->opened_path)) {
1321        ZCSG(blacklist_misses)++;
1322        *op_array_p = accelerator_orig_compile_file(file_handle, type);
1323        return NULL;
1324    }
1325
1326    if (file_handle->type == ZEND_HANDLE_STREAM &&
1327        (!strstr(file_handle->filename, ".phar") ||
1328         strstr(file_handle->filename, "://"))) {
1329        char *buf;
1330        size_t size;
1331
1332        /* Stream callbacks needs to be called in context of original
1333         * function and class tables (see: https://bugs.php.net/bug.php?id=64353)
1334         */
1335        if (zend_stream_fixup(file_handle, &buf, &size) == FAILURE) {
1336            *op_array_p = NULL;
1337            return NULL;
1338        }
1339    }
1340
1341    if (ZCG(accel_directives).validate_timestamps ||
1342        ZCG(accel_directives).file_update_protection ||
1343        ZCG(accel_directives).max_file_size > 0) {
1344        size_t size = 0;
1345
1346        /* Obtain the file timestamps, *before* actually compiling them,
1347         * otherwise we have a race-condition.
1348         */
1349        timestamp = zend_get_file_handle_timestamp(file_handle, ZCG(accel_directives).max_file_size > 0 ? &size : NULL);
1350
1351        /* If we can't obtain a timestamp (that means file is possibly socket)
1352         *  we won't cache it
1353         */
1354        if (timestamp == 0) {
1355            *op_array_p = accelerator_orig_compile_file(file_handle, type);
1356            return NULL;
1357        }
1358
1359        /* check if file is too new (may be it's not written completely yet) */
1360        if (ZCG(accel_directives).file_update_protection &&
1361            (ZCG(request_time) - ZCG(accel_directives).file_update_protection < timestamp)) {
1362            *op_array_p = accelerator_orig_compile_file(file_handle, type);
1363            return NULL;
1364        }
1365
1366        if (ZCG(accel_directives).max_file_size > 0 && size > (size_t)ZCG(accel_directives).max_file_size) {
1367            ZCSG(blacklist_misses)++;
1368            *op_array_p = accelerator_orig_compile_file(file_handle, type);
1369            return NULL;
1370        }
1371    }
1372
1373    new_persistent_script = create_persistent_script();
1374
1375    /* Save the original values for the op_array, function table and class table */
1376    orig_active_op_array = CG(active_op_array);
1377    orig_function_table = CG(function_table);
1378    orig_class_table = CG(class_table);
1379    ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler));
1380
1381    /* Override them with ours */
1382    CG(function_table) = &ZCG(function_table);
1383    EG(class_table) = CG(class_table) = &new_persistent_script->class_table;
1384    ZVAL_UNDEF(&EG(user_error_handler));
1385
1386    zend_try {
1387        orig_compiler_options = CG(compiler_options);
1388        CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
1389        CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
1390        CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
1391        CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
1392        op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type);
1393        CG(compiler_options) = orig_compiler_options;
1394    } zend_catch {
1395        op_array = NULL;
1396        do_bailout = 1;
1397        CG(compiler_options) = orig_compiler_options;
1398    } zend_end_try();
1399
1400    /* Restore originals */
1401    CG(active_op_array) = orig_active_op_array;
1402    CG(function_table) = orig_function_table;
1403    EG(class_table) = CG(class_table) = orig_class_table;
1404    EG(user_error_handler) = orig_user_error_handler;
1405
1406    if (!op_array) {
1407        /* compilation failed */
1408        free_persistent_script(new_persistent_script, 1);
1409        zend_accel_free_user_functions(&ZCG(function_table));
1410        if (do_bailout) {
1411            zend_bailout();
1412        }
1413        return NULL;
1414    }
1415
1416    /* Build the persistent_script structure.
1417       Here we aren't sure we would store it, but we will need it
1418       further anyway.
1419    */
1420    zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->function_table);
1421    new_persistent_script->main_op_array = *op_array;
1422
1423    efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
1424
1425    /* Fill in the ping_auto_globals_mask for the new script. If jit for auto globals is enabled we
1426       will have to ping the used auto global variables before execution */
1427    if (PG(auto_globals_jit)) {
1428        new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals();
1429    } else {
1430        new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit();
1431    }
1432
1433    if (ZCG(accel_directives).validate_timestamps) {
1434        /* Obtain the file timestamps, *before* actually compiling them,
1435         * otherwise we have a race-condition.
1436         */
1437        new_persistent_script->timestamp = timestamp;
1438        new_persistent_script->dynamic_members.revalidate = ZCG(request_time) + ZCG(accel_directives).revalidate_freq;
1439    }
1440
1441    if (file_handle->opened_path) {
1442        new_persistent_script->full_path = zend_string_init(file_handle->opened_path, strlen(file_handle->opened_path), 0);
1443    } else {
1444        new_persistent_script->full_path = zend_string_init(file_handle->filename, strlen(file_handle->filename), 0);
1445    }
1446    zend_string_hash_val(new_persistent_script->full_path);
1447
1448    /* Now persistent_script structure is ready in process memory */
1449    return cache_script_in_shared_memory(new_persistent_script, key, key_length, from_shared_memory);
1450}
1451
1452/* zend_compile() replacement */
1453zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
1454{
1455    zend_persistent_script *persistent_script = NULL;
1456    char *key = NULL;
1457    int key_length;
1458    int from_shared_memory; /* if the script we've got is stored in SHM */
1459
1460    if (!file_handle->filename ||
1461        !ZCG(enabled) || !accel_startup_ok ||
1462        (!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
1463        (ZCSG(restart_in_progress) && accel_restart_is_active()) ||
1464        (is_stream_path(file_handle->filename) &&
1465         !is_cacheable_stream_path(file_handle->filename))) {
1466        /* The Accelerator is disabled, act as if without the Accelerator */
1467        return accelerator_orig_compile_file(file_handle, type);
1468    }
1469
1470    /* Make sure we only increase the currently running processes semaphore
1471     * once each execution (this function can be called more than once on
1472     * each execution)
1473     */
1474    if (!ZCG(counted)) {
1475        ZCG(counted) = 1;
1476        accel_activate_add();
1477    }
1478
1479    /* In case this callback is called from include_once, require_once or it's
1480     * a main FastCGI request, the key must be already calculated, and cached
1481     * persistent script already found */
1482    if ((EG(current_execute_data) == NULL &&
1483         ZCG(cache_opline) == NULL &&
1484         file_handle->filename == SG(request_info).path_translated &&
1485         ZCG(cache_persistent_script)) ||
1486        (EG(current_execute_data) &&
1487         EG(current_execute_data)->func &&
1488         ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1489         EG(current_execute_data)->opline == ZCG(cache_opline) &&
1490         EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL &&
1491         (EG(current_execute_data)->opline->extended_value == ZEND_INCLUDE_ONCE ||
1492          EG(current_execute_data)->opline->extended_value == ZEND_REQUIRE_ONCE))) {
1493        if (!ZCG(key_len)) {
1494            return accelerator_orig_compile_file(file_handle, type);
1495        }
1496        /* persistent script was already found by overridden open() or
1497         * resolve_path() callbacks */
1498        persistent_script = ZCG(cache_persistent_script);
1499        key = ZCG(key);
1500        key_length = ZCG(key_len);
1501    } else {
1502        /* try to find cached script by key */
1503        if ((key = accel_make_persistent_key(file_handle, &key_length)) == NULL) {
1504            return accelerator_orig_compile_file(file_handle, type);
1505        }
1506        persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length);
1507        if (!persistent_script) {
1508            /* try to find cached script by full real path */
1509            zend_accel_hash_entry *bucket;
1510
1511            /* open file to resolve the path */
1512            if (file_handle->type == ZEND_HANDLE_FILENAME &&
1513                accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) {
1514                if (type == ZEND_REQUIRE) {
1515                    zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1516                    zend_bailout();
1517                } else {
1518                    zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1519                }
1520                return NULL;
1521            }
1522
1523            if (file_handle->opened_path &&
1524                (bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path, strlen(file_handle->opened_path))) != NULL) {
1525
1526                persistent_script = (zend_persistent_script *)bucket->data;
1527                if (!ZCG(accel_directives).revalidate_path &&
1528                    !persistent_script->corrupted) {
1529                    SHM_UNPROTECT();
1530                    zend_shared_alloc_lock();
1531                    zend_accel_add_key(key, key_length, bucket);
1532                    zend_shared_alloc_unlock();
1533                    SHM_PROTECT();
1534                }
1535            }
1536        }
1537    }
1538
1539    /* clear cache */
1540    ZCG(cache_opline) = NULL;
1541    ZCG(cache_persistent_script) = NULL;
1542
1543    if (persistent_script && persistent_script->corrupted) {
1544        persistent_script = NULL;
1545    }
1546
1547    SHM_UNPROTECT();
1548
1549    /* If script is found then validate_timestamps if option is enabled */
1550    if (persistent_script && ZCG(accel_directives).validate_timestamps) {
1551        if (validate_timestamp_and_record(persistent_script, file_handle) == FAILURE) {
1552            zend_shared_alloc_lock();
1553            if (!persistent_script->corrupted) {
1554                persistent_script->corrupted = 1;
1555                persistent_script->timestamp = 0;
1556                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1557                if (ZSMMG(memory_exhausted)) {
1558                    zend_accel_restart_reason reason =
1559                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1560                    zend_accel_schedule_restart_if_necessary(reason);
1561                }
1562            }
1563            zend_shared_alloc_unlock();
1564            persistent_script = NULL;
1565        }
1566    }
1567
1568    /* if turned on - check the compiled script ADLER32 checksum */
1569    if (persistent_script && ZCG(accel_directives).consistency_checks
1570        && persistent_script->dynamic_members.hits % ZCG(accel_directives).consistency_checks == 0) {
1571
1572        unsigned int checksum = zend_accel_script_checksum(persistent_script);
1573        if (checksum != persistent_script->dynamic_members.checksum ) {
1574            /* The checksum is wrong */
1575            zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s':  expected=0x%0.8X, found=0x%0.8X",
1576                             persistent_script->full_path, persistent_script->dynamic_members.checksum, checksum);
1577            zend_shared_alloc_lock();
1578            if (!persistent_script->corrupted) {
1579                persistent_script->corrupted = 1;
1580                persistent_script->timestamp = 0;
1581                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1582                if (ZSMMG(memory_exhausted)) {
1583                    zend_accel_restart_reason reason =
1584                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1585                    zend_accel_schedule_restart_if_necessary(reason);
1586                }
1587            }
1588            zend_shared_alloc_unlock();
1589            persistent_script = NULL;
1590        }
1591    }
1592
1593    /* If script was not found or invalidated by validate_timestamps */
1594    if (!persistent_script) {
1595        uint32_t old_const_num = zend_hash_next_free_element(EG(zend_constants));
1596        zend_op_array *op_array;
1597
1598        /* Cache miss.. */
1599        ZCSG(misses)++;
1600
1601        /* No memory left. Behave like without the Accelerator */
1602        if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) {
1603            SHM_PROTECT();
1604            return accelerator_orig_compile_file(file_handle, type);
1605        }
1606
1607        /* Try and cache the script and assume that it is returned from_shared_memory.
1608         * If it isn't compile_and_cache_file() changes the flag to 0
1609         */
1610        from_shared_memory = 0;
1611        persistent_script = compile_and_cache_file(file_handle, type, key, key_length, &op_array, &from_shared_memory);
1612
1613        /* Caching is disabled, returning op_array;
1614         * or something went wrong during compilation, returning NULL
1615         */
1616        if (!persistent_script) {
1617            SHM_PROTECT();
1618            return op_array;
1619        }
1620        if (from_shared_memory) {
1621            /* Delete immutable arrays moved into SHM */
1622            uint32_t new_const_num = zend_hash_next_free_element(EG(zend_constants));
1623            while (new_const_num > old_const_num) {
1624                new_const_num--;
1625                zend_hash_index_del(EG(zend_constants), new_const_num);
1626            }
1627        }
1628    } else {
1629
1630#if !ZEND_WIN32
1631        ZCSG(hits)++; /* TBFixed: may lose one hit */
1632        persistent_script->dynamic_members.hits++; /* see above */
1633#else
1634        INCREMENT(hits);
1635        InterlockedIncrement64(&persistent_script->dynamic_members.hits);
1636#endif
1637
1638        /* see bug #15471 (old BTS) */
1639        if (persistent_script->full_path) {
1640            if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
1641                !EG(current_execute_data)->func ||
1642                !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
1643                EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
1644                (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
1645                 EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
1646                if (zend_hash_add_empty_element(&EG(included_files), persistent_script->full_path) != NULL) {
1647                    /* ext/phar has to load phar's metadata into memory */
1648                    if (persistent_script->is_phar) {
1649                        php_stream_statbuf ssb;
1650                        char *fname = emalloc(sizeof("phar://") + persistent_script->full_path->len);
1651
1652                        memcpy(fname, "phar://", sizeof("phar://") - 1);
1653                        memcpy(fname + sizeof("phar://") - 1, persistent_script->full_path->val, persistent_script->full_path->len + 1);
1654                        php_stream_stat_path(fname, &ssb);
1655                        efree(fname);
1656                    }
1657                }
1658            }
1659        }
1660        zend_file_handle_dtor(file_handle);
1661        from_shared_memory = 1;
1662    }
1663
1664    persistent_script->dynamic_members.last_used = ZCG(request_time);
1665
1666    SHM_PROTECT();
1667
1668    /* Fetch jit auto globals used in the script before execution */
1669    if (persistent_script->ping_auto_globals_mask) {
1670        zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
1671    }
1672
1673    return zend_accel_load_script(persistent_script, from_shared_memory);
1674}
1675
1676/* zend_stream_open_function() replacement for PHP 5.3 and above */
1677static int persistent_stream_open_function(const char *filename, zend_file_handle *handle)
1678{
1679    if (ZCG(enabled) && accel_startup_ok &&
1680        (ZCG(counted) || ZCSG(accelerator_enabled)) &&
1681        !ZCSG(restart_in_progress)) {
1682
1683        /* check if callback is called from include_once or it's a main request */
1684        if ((!EG(current_execute_data) &&
1685             filename == SG(request_info).path_translated) ||
1686            (EG(current_execute_data) &&
1687             EG(current_execute_data)->func &&
1688             ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1689             EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL &&
1690             (EG(current_execute_data)->opline->extended_value == ZEND_INCLUDE_ONCE ||
1691              EG(current_execute_data)->opline->extended_value == ZEND_REQUIRE_ONCE))) {
1692            /* we are in include_once or FastCGI request */
1693            zend_persistent_script *persistent_script;
1694
1695            handle->filename = (char*)filename;
1696            handle->free_filename = 0;
1697
1698            /* check if cached script was already found by resolve_path() */
1699            if ((EG(current_execute_data) == NULL &&
1700                 ZCG(cache_opline) == NULL &&
1701                 ZCG(cache_persistent_script) != NULL) ||
1702                (EG(current_execute_data) &&
1703                 (ZCG(cache_opline) == EG(current_execute_data)->opline))) {
1704                persistent_script = ZCG(cache_persistent_script);
1705                handle->opened_path = estrndup(persistent_script->full_path->val, persistent_script->full_path->len);
1706                handle->type = ZEND_HANDLE_FILENAME;
1707                return SUCCESS;
1708#if 0
1709            } else {
1710                /* FIXME: It looks like this part is not needed any more */
1711                int filename_len = strlen(filename);
1712
1713                if ((IS_ABSOLUTE_PATH(filename, filename_len) ||
1714                     is_stream_path(filename)) &&
1715                    (persistent_script = zend_accel_hash_find(&ZCSG(hash), (char*)filename, filename_len)) != NULL &&
1716                    !persistent_script->corrupted) {
1717
1718                    handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len);
1719                    handle->type = ZEND_HANDLE_FILENAME;
1720                    memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len + 1);
1721                    ZCG(key_len) = persistent_script->full_path_len;
1722                    ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
1723                    ZCG(cache_persistent_script) = EG(current_execute_data) ? persistent_script : NULL;
1724                    return SUCCESS;
1725                }
1726#endif
1727            }
1728        }
1729    }
1730    ZCG(cache_opline) = NULL;
1731    ZCG(cache_persistent_script) = NULL;
1732    return accelerator_orig_zend_stream_open_function(filename, handle);
1733}
1734
1735/* zend_resolve_path() replacement for PHP 5.3 and above */
1736static char* persistent_zend_resolve_path(const char *filename, int filename_len)
1737{
1738    if (ZCG(enabled) && accel_startup_ok &&
1739        (ZCG(counted) || ZCSG(accelerator_enabled)) &&
1740        !ZCSG(restart_in_progress)) {
1741
1742        /* check if callback is called from include_once or it's a main request */
1743        if ((!EG(current_execute_data) &&
1744             filename == SG(request_info).path_translated) ||
1745            (EG(current_execute_data) &&
1746             EG(current_execute_data)->func &&
1747             ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1748             EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL &&
1749             (EG(current_execute_data)->opline->extended_value == ZEND_INCLUDE_ONCE ||
1750              EG(current_execute_data)->opline->extended_value == ZEND_REQUIRE_ONCE))) {
1751            /* we are in include_once or FastCGI request */
1752            zend_file_handle handle;
1753            char *key = NULL;
1754            int key_length;
1755            char *resolved_path;
1756            zend_accel_hash_entry *bucket;
1757            zend_persistent_script *persistent_script;
1758
1759            /* Check if requested file already cached (by full name) */
1760            if ((IS_ABSOLUTE_PATH(filename, filename_len) ||
1761                 is_stream_path(filename)) &&
1762                (bucket = zend_accel_hash_find_entry(&ZCSG(hash), (char*)filename, filename_len)) != NULL) {
1763                persistent_script = (zend_persistent_script *)bucket->data;
1764                if (persistent_script && !persistent_script->corrupted) {
1765                    memcpy(ZCG(key), persistent_script->full_path->val, persistent_script->full_path->len + 1);
1766                    ZCG(key_len) = persistent_script->full_path->len;
1767                    ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
1768                    ZCG(cache_persistent_script) = persistent_script;
1769                    return estrndup(persistent_script->full_path->val, persistent_script->full_path->len);
1770                }
1771            }
1772
1773            /* Check if requested file already cached (by key) */
1774            handle.filename = (char*)filename;
1775            handle.free_filename = 0;
1776            handle.opened_path = NULL;
1777            key = accel_make_persistent_key_ex(&handle, filename_len, &key_length);
1778            if (!ZCG(accel_directives).revalidate_path &&
1779                key &&
1780                (persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length)) != NULL &&
1781                !persistent_script->corrupted) {
1782
1783                /* we have persistent script */
1784                ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
1785                ZCG(cache_persistent_script) = persistent_script;
1786                return estrndup(persistent_script->full_path->val, persistent_script->full_path->len);
1787            }
1788
1789            /* find the full real path */
1790            resolved_path = accelerator_orig_zend_resolve_path(filename, filename_len);
1791
1792            /* Check if requested file already cached (by real path) */
1793            if (resolved_path &&
1794                (bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path, strlen(resolved_path))) != NULL) {
1795                persistent_script = (zend_persistent_script *)bucket->data;
1796
1797                if (persistent_script && !persistent_script->corrupted) {
1798                    if (key && !ZCG(accel_directives).revalidate_path) {
1799                        /* add another "key" for the same bucket */
1800                        SHM_UNPROTECT();
1801                        zend_shared_alloc_lock();
1802                        zend_accel_add_key(key, key_length, bucket);
1803                        zend_shared_alloc_unlock();
1804                        SHM_PROTECT();
1805                    }
1806                    ZCG(cache_opline) = (EG(current_execute_data) && key) ? EG(current_execute_data)->opline : NULL;
1807                    ZCG(cache_persistent_script) = key ? persistent_script : NULL;
1808                    return resolved_path;
1809                }
1810            }
1811            ZCG(cache_opline) = NULL;
1812            ZCG(cache_persistent_script) = NULL;
1813            return resolved_path;
1814        }
1815    }
1816    ZCG(cache_opline) = NULL;
1817    ZCG(cache_persistent_script) = NULL;
1818    return accelerator_orig_zend_resolve_path(filename, filename_len);
1819}
1820
1821static void zend_reset_cache_vars(void)
1822{
1823    ZSMMG(memory_exhausted) = 0;
1824    ZCSG(hits) = 0;
1825    ZCSG(misses) = 0;
1826    ZCSG(blacklist_misses) = 0;
1827    ZSMMG(wasted_shared_memory) = 0;
1828    ZCSG(restart_pending) = 0;
1829    ZCSG(force_restart_time) = 0;
1830}
1831
1832static void accel_activate(void)
1833{
1834
1835    if (!ZCG(enabled) || !accel_startup_ok) {
1836        return;
1837    }
1838
1839    if (!ZCG(function_table).nTableSize) {
1840        zend_hash_init(&ZCG(function_table), zend_hash_num_elements(CG(function_table)), NULL, ZEND_FUNCTION_DTOR, 1);
1841        zend_accel_copy_internal_functions();
1842    }
1843
1844    SHM_UNPROTECT();
1845    /* PHP-5.4 and above return "double", but we use 1 sec precision */
1846    ZCG(auto_globals_mask) = 0;
1847    ZCG(request_time) = (time_t)sapi_get_request_time();
1848    ZCG(cache_opline) = NULL;
1849    ZCG(cache_persistent_script) = NULL;
1850    ZCG(include_path_check) = !ZCG(include_path_key);
1851
1852    if (ZCG(counted)) {
1853#ifdef ZTS
1854        zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %d", tsrm_thread_id());
1855#else
1856        zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid());
1857#endif
1858        accel_unlock_all();
1859        ZCG(counted) = 0;
1860    }
1861
1862    if (ZCSG(restart_pending)) {
1863        zend_shared_alloc_lock();
1864        if (ZCSG(restart_pending) != 0) { /* check again, to ensure that the cache wasn't already cleaned by another process */
1865            if (accel_is_inactive() == SUCCESS) {
1866                zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!");
1867                ZCSG(restart_pending) = 0;
1868                switch ZCSG(restart_reason) {
1869                    case ACCEL_RESTART_OOM:
1870                        ZCSG(oom_restarts)++;
1871                        break;
1872                    case ACCEL_RESTART_HASH:
1873                        ZCSG(hash_restarts)++;
1874                        break;
1875                    case ACCEL_RESTART_USER:
1876                        ZCSG(manual_restarts)++;
1877                        break;
1878                }
1879                accel_restart_enter();
1880
1881                zend_reset_cache_vars();
1882                zend_accel_hash_clean(&ZCSG(hash));
1883
1884                /* include_paths keeps only the first path */
1885                if (ZCSG(include_paths).num_entries > 1) {
1886                    ZCSG(include_paths).num_entries = 1;
1887                    ZCSG(include_paths).num_direct_entries = 1;
1888                    memset(ZCSG(include_paths).hash_table, 0, sizeof(zend_accel_hash_entry*) * ZCSG(include_paths).max_num_entries);
1889                    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];
1890                }
1891
1892#if !defined(ZTS)
1893                if (ZCG(accel_directives).interned_strings_buffer) {
1894                    accel_interned_strings_restore_state();
1895                }
1896#endif
1897
1898                zend_shared_alloc_restore_state();
1899                ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart);
1900                ZCSG(last_restart_time) = ZCG(request_time);
1901                accel_restart_leave();
1902            }
1903        }
1904        zend_shared_alloc_unlock();
1905    }
1906
1907    /* check if ZCG(function_table) wasn't somehow polluted on the way */
1908    if (ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) {
1909        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)));
1910    }
1911
1912    ZCG(cwd) = NULL;
1913
1914    SHM_PROTECT();
1915}
1916
1917#if !ZEND_DEBUG
1918
1919/* Fast Request Shutdown
1920 * =====================
1921 * Zend Memory Manager frees memory by its own. We don't have to free each
1922 * allocated block separately, but we like to call all the destructors and
1923 * callbacks in exactly the same order.
1924 */
1925static void accel_fast_zval_dtor(zval *zvalue);
1926
1927static void accel_fast_hash_destroy(HashTable *ht)
1928{
1929    uint idx;
1930    Bucket *p;
1931
1932    for (idx = 0; idx < ht->nNumUsed; idx++) {
1933        p = ht->arData + idx;
1934        if (Z_TYPE(p->val) == IS_UNDEF) continue;
1935        accel_fast_zval_dtor(&p->val);
1936    }
1937}
1938
1939static void accel_fast_zval_dtor(zval *zvalue)
1940{
1941    if (Z_REFCOUNTED_P(zvalue) && Z_DELREF_P(zvalue) == 0) {
1942        switch (Z_TYPE_P(zvalue)) {
1943            case IS_ARRAY: {
1944                                    GC_REMOVE_FROM_BUFFER(Z_ARR_P(zvalue));
1945                    if (Z_ARR_P(zvalue) != &EG(symbol_table)) {
1946                        /* break possible cycles */
1947                        ZVAL_NULL(zvalue);
1948                        accel_fast_hash_destroy(Z_ARRVAL_P(zvalue));
1949                    }
1950                }
1951                break;
1952            case IS_OBJECT:
1953                {
1954
1955                    OBJ_RELEASE(Z_OBJ_P(zvalue));
1956                }
1957                break;
1958            case IS_RESOURCE:
1959                {
1960
1961                    /* destroy resource */
1962                    zend_list_delete(Z_RES_P(zvalue));
1963                }
1964                break;
1965            case IS_LONG:
1966            case IS_DOUBLE:
1967            case IS_FALSE:
1968            case IS_TRUE:
1969            case IS_NULL:
1970            case IS_STRING:
1971            case IS_CONSTANT:
1972            default:
1973                return;
1974                break;
1975        }
1976    }
1977}
1978
1979static int accel_clean_non_persistent_function(zval *zv)
1980{
1981    zend_function *function = Z_PTR_P(zv);
1982
1983    if (function->type == ZEND_INTERNAL_FUNCTION) {
1984        return ZEND_HASH_APPLY_STOP;
1985    } else {
1986        if (function->op_array.static_variables) {
1987            if (!(GC_FLAGS(function->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
1988                if (--GC_REFCOUNT(function->op_array.static_variables) == 0) {
1989                    accel_fast_hash_destroy(function->op_array.static_variables);
1990                }
1991            }
1992            function->op_array.static_variables = NULL;
1993        }
1994        return ZEND_HASH_APPLY_REMOVE;
1995    }
1996}
1997
1998static inline void zend_accel_fast_del_bucket(HashTable *ht, uint32_t idx, Bucket *p)
1999{
2000    uint32_t nIndex = p->h & ht->nTableMask;
2001    uint32_t i = ht->arHash[nIndex];
2002
2003    ht->nNumUsed--;
2004    ht->nNumOfElements--;
2005    if (idx != i) {
2006        Bucket *prev = ht->arData + i;
2007        while (Z_NEXT(prev->val) != idx) {
2008            i = Z_NEXT(prev->val);
2009            prev = ht->arData + i;
2010        }
2011        Z_NEXT(prev->val) = Z_NEXT(p->val);
2012    } else {
2013        ht->arHash[p->h & ht->nTableMask] = Z_NEXT(p->val);
2014    }
2015}
2016
2017static void zend_accel_fast_shutdown(void)
2018{
2019    if (EG(full_tables_cleanup)) {
2020        EG(symbol_table).pDestructor = accel_fast_zval_dtor;
2021    } else {
2022        dtor_func_t old_destructor;
2023
2024        if (EG(objects_store).top > 1 || zend_hash_num_elements(&EG(regular_list)) > 0) {
2025            /* We don't have to destroy all zvals if they cannot call any destructors */
2026
2027            old_destructor = EG(symbol_table).pDestructor;
2028            EG(symbol_table).pDestructor = accel_fast_zval_dtor;
2029            zend_try {
2030                zend_hash_graceful_reverse_destroy(&EG(symbol_table));
2031            } zend_end_try();
2032            EG(symbol_table).pDestructor = old_destructor;
2033        }
2034        zend_hash_init(&EG(symbol_table), 8, NULL, NULL, 0);
2035
2036        ZEND_HASH_REVERSE_FOREACH(EG(function_table), 0) {
2037            zend_function *func = Z_PTR(_p->val);
2038
2039            if (func->type == ZEND_INTERNAL_FUNCTION) {
2040                break;
2041            } else {
2042                if (func->op_array.static_variables) {
2043                    if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
2044                        if (--GC_REFCOUNT(func->op_array.static_variables) == 0) {
2045                            accel_fast_hash_destroy(func->op_array.static_variables);
2046                        }
2047                    }
2048                }
2049                zend_accel_fast_del_bucket(EG(function_table), _idx-1, _p);
2050            }
2051        } ZEND_HASH_FOREACH_END();
2052
2053        ZEND_HASH_REVERSE_FOREACH(EG(class_table), 0) {
2054            zend_class_entry *ce = Z_PTR(_p->val);
2055
2056            if (ce->type == ZEND_INTERNAL_CLASS) {
2057                break;
2058            } else {
2059                if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
2060                    zend_function *func;
2061
2062                    ZEND_HASH_FOREACH_PTR(&ce->function_table, func) {
2063                        if (func->type == ZEND_USER_FUNCTION) {
2064                            if (func->op_array.static_variables) {
2065                                if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
2066                                    if (--GC_REFCOUNT(func->op_array.static_variables) == 0) {
2067                                        accel_fast_hash_destroy(func->op_array.static_variables);
2068                                    }
2069                                }
2070                                func->op_array.static_variables = NULL;
2071                            }
2072                        }
2073                    } ZEND_HASH_FOREACH_END();
2074                }
2075                if (ce->static_members_table) {
2076                    int i;
2077
2078                    for (i = 0; i < ce->default_static_members_count; i++) {
2079                        accel_fast_zval_dtor(&ce->static_members_table[i]);
2080                        ZVAL_UNDEF(&ce->static_members_table[i]);
2081                    }
2082                    ce->static_members_table = NULL;
2083                }
2084                zend_accel_fast_del_bucket(EG(class_table), _idx-1, _p);
2085            }
2086        } ZEND_HASH_FOREACH_END();
2087
2088        ZEND_HASH_REVERSE_FOREACH(EG(zend_constants), 0) {
2089            zend_constant *c = Z_PTR(_p->val);
2090
2091            if (c->flags & CONST_PERSISTENT) {
2092                break;
2093            } else {
2094                zend_accel_fast_del_bucket(EG(zend_constants), _idx-1, _p);
2095            }
2096        } ZEND_HASH_FOREACH_END();
2097    }
2098    CG(unclean_shutdown) = 1;
2099}
2100#endif
2101
2102static void accel_deactivate(void)
2103{
2104    /* ensure that we restore function_table and class_table
2105     * In general, they're restored by persistent_compile_file(), but in case
2106     * the script is aborted abnormally, they may become messed up.
2107     */
2108
2109    if (!ZCG(enabled) || !accel_startup_ok) {
2110        return;
2111    }
2112
2113    zend_shared_alloc_safe_unlock(); /* be sure we didn't leave cache locked */
2114    accel_unlock_all();
2115    ZCG(counted) = 0;
2116
2117#if !ZEND_DEBUG
2118    if (ZCG(accel_directives).fast_shutdown) {
2119        zend_accel_fast_shutdown();
2120    }
2121#endif
2122
2123    if (ZCG(cwd)) {
2124        efree(ZCG(cwd));
2125        ZCG(cwd) = NULL;
2126    }
2127
2128}
2129
2130static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2)
2131{
2132    (void)element2; /* keep the compiler happy */
2133
2134    if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) {
2135        element1->startup = NULL;
2136#if 0
2137        /* We have to call shutdown callback it to free TS resources */
2138        element1->shutdown = NULL;
2139#endif
2140        element1->activate = NULL;
2141        element1->deactivate = NULL;
2142        element1->op_array_handler = NULL;
2143
2144#ifdef __DEBUG_MESSAGES__
2145        fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error"));
2146        fflush(stderr);
2147#endif
2148    }
2149
2150    return 0;
2151}
2152
2153static void zps_startup_failure(char *reason, char *api_reason, int (*cb)(zend_extension *, zend_extension *))
2154{
2155    accel_startup_ok = 0;
2156    zps_failure_reason = reason;
2157    zps_api_failure_reason = api_reason?api_reason:reason;
2158    zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb);
2159}
2160
2161static inline int accel_find_sapi(void)
2162{
2163    static const char *supported_sapis[] = {
2164        "apache",
2165        "fastcgi",
2166        "cli-server",
2167        "cgi-fcgi",
2168        "fpm-fcgi",
2169        "isapi",
2170        "apache2filter",
2171        "apache2handler",
2172        "litespeed",
2173        NULL
2174    };
2175    const char **sapi_name;
2176
2177    if (sapi_module.name) {
2178        for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
2179            if (strcmp(sapi_module.name, *sapi_name) == 0) {
2180                return SUCCESS;
2181            }
2182        }
2183        if (ZCG(accel_directives).enable_cli &&
2184            strcmp(sapi_module.name, "cli") == 0) {
2185            return SUCCESS;
2186        }
2187    }
2188
2189    return FAILURE;
2190}
2191
2192static int zend_accel_init_shm(void)
2193{
2194    zend_shared_alloc_lock();
2195
2196    accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals));
2197    if (!accel_shared_globals) {
2198        zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
2199        return FAILURE;
2200    }
2201    ZSMMG(app_shared_globals) = accel_shared_globals;
2202
2203    zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files);
2204    zend_accel_hash_init(&ZCSG(include_paths), 32);
2205
2206    ZCSG(interned_strings_start) = ZCSG(interned_strings_end) = NULL;
2207# ifndef ZTS
2208    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);
2209    if (ZCG(accel_directives).interned_strings_buffer) {
2210        ZCSG(interned_strings).nTableMask = ZCSG(interned_strings).nTableSize - 1;
2211        ZCSG(interned_strings).arData = zend_shared_alloc(ZCSG(interned_strings).nTableSize * sizeof(Bucket));
2212        ZCSG(interned_strings).arHash = (uint32_t*)zend_shared_alloc(ZCSG(interned_strings).nTableSize * sizeof(uint32_t));
2213        ZCSG(interned_strings_start) = zend_shared_alloc((ZCG(accel_directives).interned_strings_buffer * 1024 * 1024));
2214        if (!ZCSG(interned_strings).arData || !ZCSG(interned_strings_start)) {
2215            zend_accel_error(ACCEL_LOG_FATAL, ACCELERATOR_PRODUCT_NAME " cannot allocate buffer for interned strings");
2216            return FAILURE;
2217        }
2218        memset(ZCSG(interned_strings).arHash, INVALID_IDX, ZCSG(interned_strings).nTableSize * sizeof(uint32_t));
2219        ZCSG(interned_strings_end)   = ZCSG(interned_strings_start) + (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024);
2220        ZCSG(interned_strings_top)   = ZCSG(interned_strings_start);
2221
2222//      orig_interned_strings_start = CG(interned_strings_start);
2223//      orig_interned_strings_end = CG(interned_strings_end);
2224//      CG(interned_strings_start) = ZCSG(interned_strings_start);
2225//      CG(interned_strings_end) = ZCSG(interned_strings_end);
2226    }
2227# endif
2228
2229    orig_new_interned_string = zend_new_interned_string;
2230    orig_interned_strings_snapshot = zend_interned_strings_snapshot;
2231    orig_interned_strings_restore = zend_interned_strings_restore;
2232    zend_new_interned_string = accel_new_interned_string_for_php;
2233    zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
2234    zend_interned_strings_restore = accel_interned_strings_restore_for_php;
2235
2236# ifndef ZTS
2237    if (ZCG(accel_directives).interned_strings_buffer) {
2238        accel_use_shm_interned_strings();
2239        accel_interned_strings_save_state();
2240    }
2241# endif
2242
2243    zend_reset_cache_vars();
2244
2245    ZCSG(oom_restarts) = 0;
2246    ZCSG(hash_restarts) = 0;
2247    ZCSG(manual_restarts) = 0;
2248
2249    ZCSG(accelerator_enabled) = 1;
2250    ZCSG(start_time) = zend_accel_get_time();
2251    ZCSG(last_restart_time) = 0;
2252    ZCSG(restart_in_progress) = 0;
2253
2254    zend_shared_alloc_unlock();
2255
2256    return SUCCESS;
2257}
2258
2259static void accel_globals_ctor(zend_accel_globals *accel_globals)
2260{
2261#if defined(COMPILE_DL_OPCACHE) && defined(ZTS)
2262    ZEND_TSRMLS_CACHE_UPDATE();
2263#endif
2264    memset(accel_globals, 0, sizeof(zend_accel_globals));
2265}
2266
2267static void accel_globals_internal_func_dtor(zval *zv)
2268{
2269    free(Z_PTR_P(zv));
2270}
2271
2272static void accel_globals_dtor(zend_accel_globals *accel_globals)
2273{
2274    if (accel_globals->function_table.nTableSize) {
2275        accel_globals->function_table.pDestructor = accel_globals_internal_func_dtor;
2276        zend_hash_destroy(&accel_globals->function_table);
2277    }
2278}
2279
2280static int accel_startup(zend_extension *extension)
2281{
2282    zend_function *func;
2283    zend_ini_entry *ini_entry;
2284
2285#ifdef ZTS
2286    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);
2287#else
2288    accel_globals_ctor(&accel_globals);
2289#endif
2290
2291#ifdef ZEND_WIN32
2292    _setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */
2293#endif
2294
2295    if (start_accel_module() == FAILURE) {
2296        accel_startup_ok = 0;
2297        zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!");
2298        return FAILURE;
2299    }
2300
2301    /* no supported SAPI found - disable acceleration and stop initialization */
2302    if (accel_find_sapi() == FAILURE) {
2303        accel_startup_ok = 0;
2304        if (!ZCG(accel_directives).enable_cli &&
2305            strcmp(sapi_module.name, "cli") == 0) {
2306            zps_startup_failure("Opcode Caching is disabled for CLI", NULL, accelerator_remove_cb);
2307        } else {
2308            zps_startup_failure("Opcode Caching is only supported in Apache, ISAPI, FPM, FastCGI and LiteSpeed SAPIs", NULL, accelerator_remove_cb);
2309        }
2310        return SUCCESS;
2311    }
2312
2313    if (ZCG(enabled) == 0) {
2314        return SUCCESS ;
2315    }
2316/********************************************/
2317/* End of non-SHM dependent initializations */
2318/********************************************/
2319    switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) {
2320        case ALLOC_SUCCESS:
2321            if (zend_accel_init_shm() == FAILURE) {
2322                accel_startup_ok = 0;
2323                return FAILURE;
2324            }
2325            break;
2326        case ALLOC_FAILURE:
2327            accel_startup_ok = 0;
2328            zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
2329            return SUCCESS;
2330        case SUCCESSFULLY_REATTACHED:
2331            accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
2332            zend_shared_alloc_lock();
2333            orig_new_interned_string = zend_new_interned_string;
2334            orig_interned_strings_snapshot = zend_interned_strings_snapshot;
2335            orig_interned_strings_restore = zend_interned_strings_restore;
2336
2337            zend_new_interned_string = accel_new_interned_string_for_php;
2338            zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
2339            zend_interned_strings_restore = accel_interned_strings_restore_for_php;
2340#ifndef ZTS
2341            accel_use_shm_interned_strings();
2342#endif
2343            zend_shared_alloc_unlock();
2344            break;
2345        case FAILED_REATTACHED:
2346            accel_startup_ok = 0;
2347            zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
2348            return SUCCESS;
2349            break;
2350    }
2351
2352    /* from this point further, shared memory is supposed to be OK */
2353
2354    /* Init auto-global strings */
2355    zend_accel_init_auto_globals();
2356
2357    /* Override compiler */
2358    accelerator_orig_compile_file = zend_compile_file;
2359    zend_compile_file = persistent_compile_file;
2360
2361    /* Override stream opener function (to eliminate open() call caused by
2362     * include/require statements ) */
2363    accelerator_orig_zend_stream_open_function = zend_stream_open_function;
2364    zend_stream_open_function = persistent_stream_open_function;
2365
2366    /* Override path resolver function (to eliminate stat() calls caused by
2367     * include_once/require_once statements */
2368    accelerator_orig_zend_resolve_path = zend_resolve_path;
2369    zend_resolve_path = persistent_zend_resolve_path;
2370
2371    /* Override chdir() function */
2372    if ((func = zend_hash_str_find_ptr(CG(function_table), "chdir", sizeof("chdir")-1)) != NULL &&
2373        func->type == ZEND_INTERNAL_FUNCTION) {
2374        orig_chdir = func->internal_function.handler;
2375        func->internal_function.handler = ZEND_FN(accel_chdir);
2376    }
2377    ZCG(cwd) = NULL;
2378
2379    /* Override "include_path" modifier callback */
2380    if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
2381        ZCG(include_path) = INI_STR("include_path");
2382        ZCG(include_path_key) = NULL;
2383        if (ZCG(include_path) && *ZCG(include_path)) {
2384            ZCG(include_path_len) = strlen(ZCG(include_path));
2385            ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len));
2386            if (!ZCG(include_path_key) &&
2387                !zend_accel_hash_is_full(&ZCSG(include_paths))) {
2388                char *key;
2389
2390                zend_shared_alloc_lock();
2391                key = zend_shared_alloc(ZCG(include_path_len) + 2);
2392                if (key) {
2393                    memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1);
2394                    key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries;
2395                    ZCG(include_path_key) = key + ZCG(include_path_len) + 1;
2396                    zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len), 0, ZCG(include_path_key));
2397                } else {
2398                    zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
2399                }
2400                zend_shared_alloc_unlock();
2401            }
2402        } else {
2403            ZCG(include_path) = "";
2404            ZCG(include_path_len) = 0;
2405        }
2406        orig_include_path_on_modify = ini_entry->on_modify;
2407        ini_entry->on_modify = accel_include_path_on_modify;
2408    }
2409
2410    zend_shared_alloc_lock();
2411    zend_shared_alloc_save_state();
2412    zend_shared_alloc_unlock();
2413
2414    SHM_PROTECT();
2415
2416    accel_startup_ok = 1;
2417
2418    /* Override file_exists(), is_file() and is_readable() */
2419    zend_accel_override_file_functions();
2420
2421    /* Load black list */
2422    accel_blacklist.entries = NULL;
2423    if (ZCG(enabled) && accel_startup_ok &&
2424        ZCG(accel_directives).user_blacklist_filename &&
2425        *ZCG(accel_directives.user_blacklist_filename)) {
2426        zend_accel_blacklist_init(&accel_blacklist);
2427        zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
2428    }
2429
2430    return SUCCESS;
2431}
2432
2433static void accel_free_ts_resources()
2434{
2435#ifndef ZTS
2436    accel_globals_dtor(&accel_globals);
2437#else
2438    ts_free_id(accel_globals_id);
2439#endif
2440}
2441
2442void accel_shutdown(void)
2443{
2444    zend_ini_entry *ini_entry;
2445
2446    zend_accel_blacklist_shutdown(&accel_blacklist);
2447
2448    if (!ZCG(enabled) || !accel_startup_ok) {
2449        accel_free_ts_resources();
2450        return;
2451    }
2452
2453    if (ZCG(accel_directives).interned_strings_buffer) {
2454#ifndef ZTS
2455        zend_hash_clean(CG(auto_globals));
2456        zend_hash_clean(CG(function_table));
2457        zend_hash_clean(CG(class_table));
2458        zend_hash_clean(EG(zend_constants));
2459#endif
2460    }
2461
2462    zend_new_interned_string = orig_new_interned_string;
2463    zend_interned_strings_snapshot = orig_interned_strings_snapshot;
2464    zend_interned_strings_restore = orig_interned_strings_restore;
2465
2466    accel_free_ts_resources();
2467    zend_shared_alloc_shutdown();
2468    zend_compile_file = accelerator_orig_compile_file;
2469
2470    if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
2471        ini_entry->on_modify = orig_include_path_on_modify;
2472    }
2473}
2474
2475void zend_accel_schedule_restart(zend_accel_restart_reason reason)
2476{
2477    if (ZCSG(restart_pending)) {
2478        /* don't schedule twice */
2479        return;
2480    }
2481    zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled!");
2482
2483    SHM_UNPROTECT();
2484    ZCSG(restart_pending) = 1;
2485    ZCSG(restart_reason) = reason;
2486    ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled);
2487    ZCSG(accelerator_enabled) = 0;
2488
2489    if (ZCG(accel_directives).force_restart_timeout) {
2490        ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout;
2491    } else {
2492        ZCSG(force_restart_time) = 0;
2493    }
2494    SHM_PROTECT();
2495}
2496
2497/* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
2498#ifdef ZEND_WIN32
2499#define accel_deactivate_now() ZCG(counted) = 1; accel_deactivate_sub()
2500#else
2501#define accel_deactivate_now() accel_deactivate_sub()
2502#endif
2503
2504/* ensures it is OK to read SHM
2505    if it's not OK (restart in progress) returns FAILURE
2506    if OK returns SUCCESS
2507    MUST call accelerator_shm_read_unlock after done lock operations
2508*/
2509int accelerator_shm_read_lock(void)
2510{
2511    if (ZCG(counted)) {
2512        /* counted means we are holding read lock for SHM, so that nothing bad can happen */
2513        return SUCCESS;
2514    } else {
2515        /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled
2516            or is in progress now */
2517        accel_activate_add(); /* acquire usage lock */
2518        /* Now if we weren't inside restart, restart would not begin until we remove usage lock */
2519        if (ZCSG(restart_in_progress)) {
2520            /* we already were inside restart this means it's not safe to touch shm */
2521            accel_deactivate_now(); /* drop usage lock */
2522            return FAILURE;
2523        }
2524    }
2525    return SUCCESS;
2526}
2527
2528/* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */
2529void accelerator_shm_read_unlock(void)
2530{
2531    if (!ZCG(counted)) {
2532        /* counted is 0 - meaning we had to readlock manually, release readlock now */
2533        accel_deactivate_now();
2534    }
2535}
2536
2537ZEND_EXT_API zend_extension zend_extension_entry = {
2538    ACCELERATOR_PRODUCT_NAME,               /* name */
2539    ACCELERATOR_VERSION,                    /* version */
2540    "Zend Technologies",                    /* author */
2541    "http://www.zend.com/",                 /* URL */
2542    "Copyright (c) 1999-2015",              /* copyright */
2543    accel_startup,                          /* startup */
2544    NULL,                                   /* shutdown */
2545    accel_activate,                         /* per-script activation */
2546    accel_deactivate,                       /* per-script deactivation */
2547    NULL,                                   /* message handler */
2548    NULL,                                   /* op_array handler */
2549    NULL,                                   /* extended statement handler */
2550    NULL,                                   /* extended fcall begin handler */
2551    NULL,                                   /* extended fcall end handler */
2552    NULL,                                   /* op_array ctor */
2553    NULL,                                   /* op_array dtor */
2554    STANDARD_ZEND_EXTENSION_PROPERTIES
2555};
2556