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