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