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