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