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