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 <time.h>
23 
24 #include "php.h"
25 #include "ZendAccelerator.h"
26 #include "zend_API.h"
27 #include "zend_shared_alloc.h"
28 #include "zend_accelerator_blacklist.h"
29 #include "php_ini.h"
30 #include "SAPI.h"
31 #if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO
32 # include "zend_virtual_cwd.h"
33 #else
34 # include "TSRM/tsrm_virtual_cwd.h"
35 #endif
36 #include "ext/standard/info.h"
37 #include "ext/standard/php_filestat.h"
38 
39 #define STRING_NOT_NULL(s) (NULL == (s)?"":s)
40 #define MIN_ACCEL_FILES 200
41 #define MAX_ACCEL_FILES 1000000
42 #define TOKENTOSTR(X) #X
43 
44 static void (*orig_file_exists)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
45 static void (*orig_is_file)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
46 static void (*orig_is_readable)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
47 
48 ZEND_BEGIN_ARG_INFO(arginfo_opcache_none, 0)
49 ZEND_END_ARG_INFO()
50 
51 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_get_status, 0, 0, 0)
52 	ZEND_ARG_INFO(0, fetch_scripts)
53 ZEND_END_ARG_INFO()
54 
55 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_compile_file, 0, 0, 1)
56 	ZEND_ARG_INFO(0, file)
57 ZEND_END_ARG_INFO()
58 
59 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_invalidate, 0, 0, 1)
60 	ZEND_ARG_INFO(0, script)
61 	ZEND_ARG_INFO(0, force)
62 ZEND_END_ARG_INFO()
63 
64 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_is_script_cached, 0, 0, 1)
65 	ZEND_ARG_INFO(0, script)
66 ZEND_END_ARG_INFO()
67 
68 /* User functions */
69 static ZEND_FUNCTION(opcache_reset);
70 static ZEND_FUNCTION(opcache_invalidate);
71 static ZEND_FUNCTION(opcache_is_script_cached);
72 
73 /* Private functions */
74 static ZEND_FUNCTION(opcache_get_status);
75 static ZEND_FUNCTION(opcache_compile_file);
76 static ZEND_FUNCTION(opcache_get_configuration);
77 
78 static zend_function_entry accel_functions[] = {
79 	/* User functions */
80 	ZEND_FE(opcache_reset,					arginfo_opcache_none)
81 	ZEND_FE(opcache_invalidate,				arginfo_opcache_invalidate)
82 	ZEND_FE(opcache_compile_file,			arginfo_opcache_compile_file)
83 	ZEND_FE(opcache_is_script_cached,		arginfo_opcache_is_script_cached)
84 	/* Private functions */
85 	ZEND_FE(opcache_get_configuration,		arginfo_opcache_none)
86 	ZEND_FE(opcache_get_status,				arginfo_opcache_get_status)
87 	{ NULL, NULL, NULL, 0, 0 }
88 };
89 
validate_api_restriction(TSRMLS_D)90 static int validate_api_restriction(TSRMLS_D)
91 {
92 	if (ZCG(accel_directives).restrict_api && *ZCG(accel_directives).restrict_api) {
93 		int len = strlen(ZCG(accel_directives).restrict_api);
94 
95 		if (!SG(request_info).path_translated ||
96 		    strlen(SG(request_info).path_translated) < len ||
97 		    memcmp(SG(request_info).path_translated, ZCG(accel_directives).restrict_api, len) != 0) {
98 			zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " API is restricted by \"restrict_api\" configuration directive");
99 			return 0;
100 		}
101 	}
102 	return 1;
103 }
104 
ZEND_INI_MH(OnUpdateMemoryConsumption)105 static ZEND_INI_MH(OnUpdateMemoryConsumption)
106 {
107 	long *p;
108 	long memsize;
109 #ifndef ZTS
110 	char *base = (char *) mh_arg2;
111 #else
112 	char *base = (char *) ts_resource(*((int *) mh_arg2));
113 #endif
114 
115 	/* keep the compiler happy */
116 	(void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
117 
118 	p = (long *) (base + (size_t)mh_arg1);
119 	memsize = atoi(new_value);
120 	/* sanity check we must use at least 8 MB */
121 	if (memsize < 8) {
122 		const char *new_new_value = "8";
123 		zend_ini_entry *ini_entry;
124 
125 		memsize = 8;
126 		zend_accel_error(ACCEL_LOG_WARNING, "opcache.memory_consumption is set below the required 8MB.\n");
127 		zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal 8MB configuration.\n");
128 
129 		if (zend_hash_find(EG(ini_directives),
130 					"opcache.memory_consumption",
131 					sizeof("opcache.memory_consumption"),
132 					(void *) &ini_entry) == FAILURE) {
133 			return FAILURE;
134 		}
135 
136 		ini_entry->value = strdup(new_new_value);
137 		ini_entry->value_length = strlen(new_new_value);
138 	}
139 	*p = memsize * (1024 * 1024);
140 	return SUCCESS;
141 }
142 
ZEND_INI_MH(OnUpdateMaxAcceleratedFiles)143 static ZEND_INI_MH(OnUpdateMaxAcceleratedFiles)
144 {
145 	long *p;
146 	long size;
147 #ifndef ZTS
148 	char *base = (char *) mh_arg2;
149 #else
150 	char *base = (char *) ts_resource(*((int *) mh_arg2));
151 #endif
152 
153 	/* keep the compiler happy */
154 	(void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
155 
156 	p = (long *) (base + (size_t)mh_arg1);
157 	size = atoi(new_value);
158 	/* sanity check we must use a value between MIN_ACCEL_FILES and MAX_ACCEL_FILES */
159 
160 	if (size < MIN_ACCEL_FILES || size > MAX_ACCEL_FILES) {
161 		const char *new_new_value;
162 		zend_ini_entry *ini_entry;
163 
164 		if (size < MIN_ACCEL_FILES) {
165 			size = MIN_ACCEL_FILES;
166 			new_new_value = TOKENTOSTR(MIN_ACCEL_FILES);
167 			zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_accelerated_files is set below the required minimum (%d).\n", MIN_ACCEL_FILES);
168 			zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal configuration.\n");
169 		}
170 		if (size > MAX_ACCEL_FILES) {
171 			size = MAX_ACCEL_FILES;
172 			new_new_value = TOKENTOSTR(MAX_ACCEL_FILES);
173 			zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_accelerated_files is set above the limit (%d).\n", MAX_ACCEL_FILES);
174 			zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the maximal configuration.\n");
175 		}
176 		if (zend_hash_find(EG(ini_directives),
177 					"opcache.max_accelerated_files",
178 					sizeof("opcache.max_accelerated_files"),
179 					(void *) &ini_entry) == FAILURE) {
180 			return FAILURE;
181 		}
182 		ini_entry->value = strdup(new_new_value);
183 		ini_entry->value_length = strlen(new_new_value);
184 	}
185 	*p = size;
186 	return SUCCESS;
187 }
188 
ZEND_INI_MH(OnUpdateMaxWastedPercentage)189 static ZEND_INI_MH(OnUpdateMaxWastedPercentage)
190 {
191 	double *p;
192 	long percentage;
193 #ifndef ZTS
194 	char *base = (char *) mh_arg2;
195 #else
196 	char *base = (char *) ts_resource(*((int *) mh_arg2));
197 #endif
198 
199 	/* keep the compiler happy */
200 	(void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
201 
202 	p = (double *) (base + (size_t)mh_arg1);
203 	percentage = atoi(new_value);
204 
205 	if (percentage <= 0 || percentage > 50) {
206 		const char *new_new_value = "5";
207 		zend_ini_entry *ini_entry;
208 
209 		percentage = 5;
210 		zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_wasted_percentage must be set between 1 and 50.\n");
211 		zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use 5%.\n");
212 		if (zend_hash_find(EG(ini_directives),
213 					"opcache.max_wasted_percentage",
214 					sizeof("opcache.max_wasted_percentage"),
215 					(void *) &ini_entry) == FAILURE) {
216 			return FAILURE;
217 		}
218 		ini_entry->value = strdup(new_new_value);
219 		ini_entry->value_length = strlen(new_new_value);
220 	}
221 	*p = (double)percentage / 100.0;
222 	return SUCCESS;
223 }
224 
ZEND_INI_MH(OnEnable)225 static ZEND_INI_MH(OnEnable)
226 {
227 	if (stage == ZEND_INI_STAGE_STARTUP ||
228 	    stage == ZEND_INI_STAGE_SHUTDOWN ||
229 	    stage == ZEND_INI_STAGE_DEACTIVATE) {
230 		return OnUpdateBool(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
231 	} else {
232 		/* It may be only temporary disabled */
233 		zend_bool *p;
234 #ifndef ZTS
235 		char *base = (char *) mh_arg2;
236 #else
237 		char *base = (char *) ts_resource(*((int *) mh_arg2));
238 #endif
239 
240 		p = (zend_bool *) (base+(size_t) mh_arg1);
241 		if ((new_value_length == 2 && strcasecmp("on", new_value) == 0) ||
242 		    (new_value_length == 3 && strcasecmp("yes", new_value) == 0) ||
243 		    (new_value_length == 4 && strcasecmp("true", new_value) == 0) ||
244 			atoi(new_value) != 0) {
245 			zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " can't be temporary enabled (it may be only disabled till the end of request)");
246 			return FAILURE;
247 		} else {
248 			*p = 0;
249 			return SUCCESS;
250 		}
251 	}
252 }
253 
254 ZEND_INI_BEGIN()
255     STD_PHP_INI_BOOLEAN("opcache.enable"             , "1", PHP_INI_ALL,    OnEnable,     enabled                             , zend_accel_globals, accel_globals)
256 	STD_PHP_INI_BOOLEAN("opcache.use_cwd"            , "1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.use_cwd            , zend_accel_globals, accel_globals)
257 	STD_PHP_INI_BOOLEAN("opcache.validate_timestamps", "1", PHP_INI_ALL   , OnUpdateBool, accel_directives.validate_timestamps, zend_accel_globals, accel_globals)
258 	STD_PHP_INI_BOOLEAN("opcache.validate_permission", "0", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.validate_permission, zend_accel_globals, accel_globals)
259 #ifndef ZEND_WIN32
260 	STD_PHP_INI_BOOLEAN("opcache.validate_root"      , "0", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.validate_root      , zend_accel_globals, accel_globals)
261 #endif
262 	STD_PHP_INI_BOOLEAN("opcache.inherited_hack"     , "1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.inherited_hack     , zend_accel_globals, accel_globals)
263 	STD_PHP_INI_BOOLEAN("opcache.dups_fix"           , "0", PHP_INI_ALL   , OnUpdateBool, accel_directives.ignore_dups        , zend_accel_globals, accel_globals)
264 	STD_PHP_INI_BOOLEAN("opcache.revalidate_path"    , "0", PHP_INI_ALL   , OnUpdateBool, accel_directives.revalidate_path    , zend_accel_globals, accel_globals)
265 
266 	STD_PHP_INI_ENTRY("opcache.log_verbosity_level"   , "1"   , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.log_verbosity_level,       zend_accel_globals, accel_globals)
267 	STD_PHP_INI_ENTRY("opcache.memory_consumption"    , "64"  , PHP_INI_SYSTEM, OnUpdateMemoryConsumption,    accel_directives.memory_consumption,        zend_accel_globals, accel_globals)
268 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
269 	STD_PHP_INI_ENTRY("opcache.interned_strings_buffer", "4"  , PHP_INI_SYSTEM, OnUpdateLong,                 accel_directives.interned_strings_buffer,   zend_accel_globals, accel_globals)
270 #endif
271 	STD_PHP_INI_ENTRY("opcache.max_accelerated_files" , "2000", PHP_INI_SYSTEM, OnUpdateMaxAcceleratedFiles,	 accel_directives.max_accelerated_files,     zend_accel_globals, accel_globals)
272 	STD_PHP_INI_ENTRY("opcache.max_wasted_percentage" , "5"   , PHP_INI_SYSTEM, OnUpdateMaxWastedPercentage,	 accel_directives.max_wasted_percentage,     zend_accel_globals, accel_globals)
273 	STD_PHP_INI_ENTRY("opcache.consistency_checks"    , "0"   , PHP_INI_ALL   , OnUpdateLong,	             accel_directives.consistency_checks,        zend_accel_globals, accel_globals)
274 	STD_PHP_INI_ENTRY("opcache.force_restart_timeout" , "180" , PHP_INI_SYSTEM, OnUpdateLong,	             accel_directives.force_restart_timeout,     zend_accel_globals, accel_globals)
275 	STD_PHP_INI_ENTRY("opcache.revalidate_freq"       , "2"   , PHP_INI_ALL   , OnUpdateLong,	             accel_directives.revalidate_freq,           zend_accel_globals, accel_globals)
276 	STD_PHP_INI_ENTRY("opcache.file_update_protection", "2"   , PHP_INI_ALL   , OnUpdateLong,                accel_directives.file_update_protection,    zend_accel_globals, accel_globals)
277 	STD_PHP_INI_ENTRY("opcache.preferred_memory_model", ""    , PHP_INI_SYSTEM, OnUpdateStringUnempty,       accel_directives.memory_model,              zend_accel_globals, accel_globals)
278 	STD_PHP_INI_ENTRY("opcache.blacklist_filename"    , ""    , PHP_INI_SYSTEM, OnUpdateString,	             accel_directives.user_blacklist_filename,   zend_accel_globals, accel_globals)
279 	STD_PHP_INI_ENTRY("opcache.max_file_size"         , "0"   , PHP_INI_SYSTEM, OnUpdateLong,	             accel_directives.max_file_size,             zend_accel_globals, accel_globals)
280 
281 	STD_PHP_INI_ENTRY("opcache.protect_memory"        , "0"  , PHP_INI_SYSTEM, OnUpdateBool,                  accel_directives.protect_memory,            zend_accel_globals, accel_globals)
282 	STD_PHP_INI_ENTRY("opcache.save_comments"         , "1"  , PHP_INI_SYSTEM, OnUpdateBool,                  accel_directives.save_comments,             zend_accel_globals, accel_globals)
283 	STD_PHP_INI_ENTRY("opcache.load_comments"         , "1"  , PHP_INI_ALL,    OnUpdateBool,                  accel_directives.load_comments,             zend_accel_globals, accel_globals)
284 	STD_PHP_INI_ENTRY("opcache.fast_shutdown"         , "0"  , PHP_INI_SYSTEM, OnUpdateBool,                  accel_directives.fast_shutdown,             zend_accel_globals, accel_globals)
285 
286 	STD_PHP_INI_ENTRY("opcache.optimization_level"    , DEFAULT_OPTIMIZATION_LEVEL , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.optimization_level,   zend_accel_globals, accel_globals)
287 	STD_PHP_INI_BOOLEAN("opcache.enable_file_override"	, "0"   , PHP_INI_SYSTEM, OnUpdateBool,              accel_directives.file_override_enabled,     zend_accel_globals, accel_globals)
288 	STD_PHP_INI_BOOLEAN("opcache.enable_cli"             , "0"   , PHP_INI_SYSTEM, OnUpdateBool,              accel_directives.enable_cli,                zend_accel_globals, accel_globals)
289 	STD_PHP_INI_ENTRY("opcache.error_log"                , ""    , PHP_INI_SYSTEM, OnUpdateString,	         accel_directives.error_log,                 zend_accel_globals, accel_globals)
290 	STD_PHP_INI_ENTRY("opcache.restrict_api"             , ""    , PHP_INI_SYSTEM, OnUpdateString,	         accel_directives.restrict_api,              zend_accel_globals, accel_globals)
291 
292 #ifdef ZEND_WIN32
293 	STD_PHP_INI_ENTRY("opcache.mmap_base", NULL, PHP_INI_SYSTEM,	OnUpdateString,	                             accel_directives.mmap_base,                 zend_accel_globals, accel_globals)
294 #endif
ZEND_INI_END()295 ZEND_INI_END()
296 
297 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
298 
299 #undef  EX
300 #define EX(element) execute_data->element
301 #define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset))
302 
303 static int ZEND_DECLARE_INHERITED_CLASS_DELAYED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
304 {
305 	zend_class_entry **pce, **pce_orig;
306 
307 	if (zend_hash_find(EG(class_table), Z_STRVAL(EX(opline)->op2.u.constant), Z_STRLEN(EX(opline)->op2.u.constant) + 1, (void **)&pce) == FAILURE ||
308 	    (zend_hash_find(EG(class_table), Z_STRVAL(EX(opline)->op1.u.constant), Z_STRLEN(EX(opline)->op1.u.constant), (void**)&pce_orig) == SUCCESS &&
309 	     *pce != *pce_orig)) {
310 		do_bind_inherited_class(EX(opline), EG(class_table), EX_T(EX(opline)->extended_value).class_entry, 0 TSRMLS_CC);
311 	}
312 	EX(opline)++;
313 	return ZEND_USER_OPCODE_CONTINUE;
314 }
315 #endif
316 
filename_is_in_cache(char * filename,int filename_len TSRMLS_DC)317 static int filename_is_in_cache(char *filename, int filename_len TSRMLS_DC)
318 {
319 	char *key;
320 	int key_length;
321 	zend_file_handle handle = {0};
322 	zend_persistent_script *persistent_script;
323 
324 	handle.filename = filename;
325 	handle.type = ZEND_HANDLE_FILENAME;
326 
327 	if (IS_ABSOLUTE_PATH(filename, filename_len)) {
328 		persistent_script = zend_accel_hash_find(&ZCSG(hash), filename, filename_len + 1);
329 		if (persistent_script) {
330 			return !persistent_script->corrupted &&
331 				(!ZCG(accel_directives).validate_timestamps ||
332 				validate_timestamp_and_record(persistent_script, &handle TSRMLS_CC) == SUCCESS);
333 		}
334 	}
335 
336 	if ((key = accel_make_persistent_key_ex(&handle, filename_len, &key_length TSRMLS_CC)) != NULL) {
337 		persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length + 1);
338 		return persistent_script && !persistent_script->corrupted &&
339 			(!ZCG(accel_directives).validate_timestamps ||
340 			validate_timestamp_and_record(persistent_script, &handle TSRMLS_CC) == SUCCESS);
341 	}
342 
343 	return 0;
344 }
345 
accel_file_in_cache(INTERNAL_FUNCTION_PARAMETERS)346 static int accel_file_in_cache(INTERNAL_FUNCTION_PARAMETERS)
347 {
348 	zval **zfilename;
349 
350 	if (ZEND_NUM_ARGS() != 1 ||
351 	    zend_get_parameters_array_ex(1, &zfilename) == FAILURE ||
352 	    Z_TYPE_PP(zfilename) != IS_STRING ||
353 	    Z_STRLEN_PP(zfilename) == 0) {
354 		return 0;
355 	}
356 	return filename_is_in_cache(Z_STRVAL_PP(zfilename), Z_STRLEN_PP(zfilename) TSRMLS_CC);
357 }
358 
accel_file_exists(INTERNAL_FUNCTION_PARAMETERS)359 static void accel_file_exists(INTERNAL_FUNCTION_PARAMETERS)
360 {
361 	if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) {
362 		RETURN_TRUE;
363 	} else {
364 		orig_file_exists(INTERNAL_FUNCTION_PARAM_PASSTHRU);
365 	}
366 }
367 
accel_is_file(INTERNAL_FUNCTION_PARAMETERS)368 static void accel_is_file(INTERNAL_FUNCTION_PARAMETERS)
369 {
370 	if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) {
371 		RETURN_TRUE;
372 	} else {
373 		orig_is_file(INTERNAL_FUNCTION_PARAM_PASSTHRU);
374 	}
375 }
376 
accel_is_readable(INTERNAL_FUNCTION_PARAMETERS)377 static void accel_is_readable(INTERNAL_FUNCTION_PARAMETERS)
378 {
379 	if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) {
380 		RETURN_TRUE;
381 	} else {
382 		orig_is_readable(INTERNAL_FUNCTION_PARAM_PASSTHRU);
383 	}
384 }
385 
ZEND_MINIT_FUNCTION(zend_accelerator)386 static ZEND_MINIT_FUNCTION(zend_accelerator)
387 {
388 	(void)type; /* keep the compiler happy */
389 
390 	REGISTER_INI_ENTRIES();
391 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
392 	zend_set_user_opcode_handler(ZEND_DECLARE_INHERITED_CLASS_DELAYED, ZEND_DECLARE_INHERITED_CLASS_DELAYED_HANDLER);
393 #endif
394 	return SUCCESS;
395 }
396 
zend_accel_override_file_functions(TSRMLS_D)397 void zend_accel_override_file_functions(TSRMLS_D)
398 {
399 	zend_function *old_function;
400 	if (ZCG(enabled) && accel_startup_ok && ZCG(accel_directives).file_override_enabled) {
401 		/* override file_exists */
402 		if (zend_hash_find(CG(function_table), "file_exists", sizeof("file_exists"), (void **)&old_function) == SUCCESS) {
403 			orig_file_exists = old_function->internal_function.handler;
404 			old_function->internal_function.handler = accel_file_exists;
405 		}
406 		if (zend_hash_find(CG(function_table), "is_file", sizeof("is_file"), (void **)&old_function) == SUCCESS) {
407 			orig_is_file = old_function->internal_function.handler;
408 			old_function->internal_function.handler = accel_is_file;
409 		}
410 		if (zend_hash_find(CG(function_table), "is_readable", sizeof("is_readable"), (void **)&old_function) == SUCCESS) {
411 			orig_is_readable = old_function->internal_function.handler;
412 			old_function->internal_function.handler = accel_is_readable;
413 		}
414 	}
415 }
416 
ZEND_MSHUTDOWN_FUNCTION(zend_accelerator)417 static ZEND_MSHUTDOWN_FUNCTION(zend_accelerator)
418 {
419 	(void)type; /* keep the compiler happy */
420 
421 	UNREGISTER_INI_ENTRIES();
422 	accel_shutdown(TSRMLS_C);
423 	return SUCCESS;
424 }
425 
zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS)426 void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS)
427 {
428 	php_info_print_table_start();
429 
430 	if (ZCG(enabled) && accel_startup_ok && (ZCG(counted) || ZCSG(accelerator_enabled))) {
431 		php_info_print_table_row(2, "Opcode Caching", "Up and Running");
432 	} else {
433 		php_info_print_table_row(2, "Opcode Caching", "Disabled");
434 	}
435 	if (ZCG(enabled) && accel_startup_ok && ZCSG(accelerator_enabled) && ZCG(accel_directives).optimization_level) {
436 		php_info_print_table_row(2, "Optimization", "Enabled");
437 	} else {
438 		php_info_print_table_row(2, "Optimization", "Disabled");
439 	}
440 	if (ZCG(enabled)) {
441 		if (!accel_startup_ok || zps_api_failure_reason) {
442 			php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason);
443 		} else {
444 			char buf[32];
445 			php_info_print_table_row(2, "Startup", "OK");
446 			php_info_print_table_row(2, "Shared memory model", zend_accel_get_shared_model());
447 			snprintf(buf, sizeof(buf), "%ld", ZCSG(hits));
448 			php_info_print_table_row(2, "Cache hits", buf);
449 			snprintf(buf, sizeof(buf), "%ld", ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses));
450 			php_info_print_table_row(2, "Cache misses", buf);
451 			snprintf(buf, sizeof(buf), "%ld", ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory));
452 			php_info_print_table_row(2, "Used memory", buf);
453 			snprintf(buf, sizeof(buf), "%ld", zend_shared_alloc_get_free_memory());
454 			php_info_print_table_row(2, "Free memory", buf);
455 			snprintf(buf, sizeof(buf), "%ld", ZSMMG(wasted_shared_memory));
456 			php_info_print_table_row(2, "Wasted memory", buf);
457 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
458 			if (ZCSG(interned_strings_start) && ZCSG(interned_strings_end) && ZCSG(interned_strings_top)) {
459 				snprintf(buf, sizeof(buf), "%ld", ZCSG(interned_strings_top) - ZCSG(interned_strings_start));
460 				php_info_print_table_row(2, "Interned Strings Used memory", buf);
461 				snprintf(buf, sizeof(buf), "%ld", ZCSG(interned_strings_end) - ZCSG(interned_strings_top));
462 				php_info_print_table_row(2, "Interned Strings Free memory", buf);
463 			}
464 #endif
465 			snprintf(buf, sizeof(buf), "%ld", ZCSG(hash).num_direct_entries);
466 			php_info_print_table_row(2, "Cached scripts", buf);
467 			snprintf(buf, sizeof(buf), "%ld", ZCSG(hash).num_entries);
468 			php_info_print_table_row(2, "Cached keys", buf);
469 			snprintf(buf, sizeof(buf), "%ld", ZCSG(hash).max_num_entries);
470 			php_info_print_table_row(2, "Max keys", buf);
471 			snprintf(buf, sizeof(buf), "%ld", ZCSG(oom_restarts));
472 			php_info_print_table_row(2, "OOM restarts", buf);
473 			snprintf(buf, sizeof(buf), "%ld", ZCSG(hash_restarts));
474 			php_info_print_table_row(2, "Hash keys restarts", buf);
475 			snprintf(buf, sizeof(buf), "%ld", ZCSG(manual_restarts));
476 			php_info_print_table_row(2, "Manual restarts", buf);
477 		}
478 	}
479 
480 	php_info_print_table_end();
481 	DISPLAY_INI_ENTRIES();
482 }
483 
484 static zend_module_entry accel_module_entry = {
485 	STANDARD_MODULE_HEADER,
486 	ACCELERATOR_PRODUCT_NAME,
487 	accel_functions,
488 	ZEND_MINIT(zend_accelerator),
489 	ZEND_MSHUTDOWN(zend_accelerator),
490 	NULL,
491 	NULL,
492 	zend_accel_info,
493     ACCELERATOR_VERSION "FE",
494 	STANDARD_MODULE_PROPERTIES
495 };
496 
start_accel_module(void)497 int start_accel_module(void)
498 {
499 	return zend_startup_module(&accel_module_entry);
500 }
501 
502 /* {{{ proto array accelerator_get_scripts()
503    Get the scripts which are accelerated by ZendAccelerator */
accelerator_get_scripts(TSRMLS_D)504 static zval* accelerator_get_scripts(TSRMLS_D)
505 {
506 	uint i;
507 	zval *return_value,*persistent_script_report;
508 	zend_accel_hash_entry *cache_entry;
509 	struct tm *ta;
510 	struct timeval exec_time;
511 	struct timeval fetch_time;
512 
513 	if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock(TSRMLS_C) != SUCCESS) {
514 		return 0;
515 	}
516 
517 	MAKE_STD_ZVAL(return_value);
518 	array_init(return_value);
519 	for (i = 0; i<ZCSG(hash).max_num_entries; i++) {
520 		for (cache_entry = ZCSG(hash).hash_table[i]; cache_entry; cache_entry = cache_entry->next) {
521 			zend_persistent_script *script;
522 			char *str;
523 			size_t len;
524 
525 			if (cache_entry->indirect) continue;
526 
527 			script = (zend_persistent_script *)cache_entry->data;
528 
529 			MAKE_STD_ZVAL(persistent_script_report);
530 			array_init(persistent_script_report);
531 			add_assoc_stringl(persistent_script_report, "full_path", script->full_path, script->full_path_len, 1);
532 			add_assoc_long(persistent_script_report, "hits", script->dynamic_members.hits);
533 			add_assoc_long(persistent_script_report, "memory_consumption", script->dynamic_members.memory_consumption);
534 			ta = localtime(&script->dynamic_members.last_used);
535 			str = asctime(ta);
536 			len = strlen(str);
537 			if (len > 0 && str[len - 1] == '\n') len--;
538 			add_assoc_stringl(persistent_script_report, "last_used", str, len, 1);
539 			add_assoc_long(persistent_script_report, "last_used_timestamp", script->dynamic_members.last_used);
540 			if (ZCG(accel_directives).validate_timestamps) {
541 				add_assoc_long(persistent_script_report, "timestamp", (long)script->timestamp);
542 			}
543 			timerclear(&exec_time);
544 			timerclear(&fetch_time);
545 
546 			zend_hash_update(return_value->value.ht, cache_entry->key, cache_entry->key_length, &persistent_script_report, sizeof(zval *), NULL);
547 		}
548 	}
549 	accelerator_shm_read_unlock(TSRMLS_C);
550 
551 	return return_value;
552 }
553 
554 /* {{{ proto array accelerator_get_status([bool fetch_scripts])
555    Obtain statistics information regarding code acceleration */
ZEND_FUNCTION(opcache_get_status)556 static ZEND_FUNCTION(opcache_get_status)
557 {
558 	long reqs;
559 	zval *memory_usage,*statistics,*scripts;
560 	zend_bool fetch_scripts = 1;
561 
562 	/* keep the compiler happy */
563 	(void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used;
564 
565 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &fetch_scripts) == FAILURE) {
566 		return;
567 	}
568 
569 	if (!validate_api_restriction(TSRMLS_C)) {
570 		RETURN_FALSE;
571 	}
572 
573 	if (!accel_startup_ok) {
574 		RETURN_FALSE;
575 	}
576 
577 	array_init(return_value);
578 
579 	/* Trivia */
580 	add_assoc_bool(return_value, "opcache_enabled", ZCG(enabled) && (ZCG(counted) || ZCSG(accelerator_enabled)));
581 	add_assoc_bool(return_value, "cache_full", ZSMMG(memory_exhausted));
582 	add_assoc_bool(return_value, "restart_pending", ZCSG(restart_pending));
583 	add_assoc_bool(return_value, "restart_in_progress", ZCSG(restart_in_progress));
584 
585 	/* Memory usage statistics */
586 	MAKE_STD_ZVAL(memory_usage);
587 	array_init(memory_usage);
588 	add_assoc_long(memory_usage, "used_memory", ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory));
589 	add_assoc_long(memory_usage, "free_memory", zend_shared_alloc_get_free_memory());
590 	add_assoc_long(memory_usage, "wasted_memory", ZSMMG(wasted_shared_memory));
591 	add_assoc_double(memory_usage, "current_wasted_percentage", (((double) ZSMMG(wasted_shared_memory))/ZCG(accel_directives).memory_consumption)*100.0);
592 	add_assoc_zval(return_value, "memory_usage", memory_usage);
593 
594 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
595 	if (ZCSG(interned_strings_start) && ZCSG(interned_strings_end) && ZCSG(interned_strings_top)) {
596 		zval *interned_strings_usage;
597 
598 		MAKE_STD_ZVAL(interned_strings_usage);
599 		array_init(interned_strings_usage);
600 		add_assoc_long(interned_strings_usage, "buffer_size", ZCSG(interned_strings_end) - ZCSG(interned_strings_start));
601 		add_assoc_long(interned_strings_usage, "used_memory", ZCSG(interned_strings_top) - ZCSG(interned_strings_start));
602 		add_assoc_long(interned_strings_usage, "free_memory", ZCSG(interned_strings_end) - ZCSG(interned_strings_top));
603 		add_assoc_long(interned_strings_usage, "number_of_strings", ZCSG(interned_strings).nNumOfElements);
604 		add_assoc_zval(return_value, "interned_strings_usage", interned_strings_usage);
605 	}
606 #endif
607 
608 	/* Accelerator statistics */
609 	MAKE_STD_ZVAL(statistics);
610 	array_init(statistics);
611 	add_assoc_long(statistics, "num_cached_scripts", ZCSG(hash).num_direct_entries);
612 	add_assoc_long(statistics, "num_cached_keys",    ZCSG(hash).num_entries);
613 	add_assoc_long(statistics, "max_cached_keys",    ZCSG(hash).max_num_entries);
614 	add_assoc_long(statistics, "hits", ZCSG(hits));
615 	add_assoc_long(statistics, "start_time", ZCSG(start_time));
616 	add_assoc_long(statistics, "last_restart_time", ZCSG(last_restart_time));
617 	add_assoc_long(statistics, "oom_restarts", ZCSG(oom_restarts));
618 	add_assoc_long(statistics, "hash_restarts", ZCSG(hash_restarts));
619 	add_assoc_long(statistics, "manual_restarts", ZCSG(manual_restarts));
620 	add_assoc_long(statistics, "misses", ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses));
621 	add_assoc_long(statistics, "blacklist_misses", ZCSG(blacklist_misses));
622 	reqs = ZCSG(hits)+ZCSG(misses);
623 	add_assoc_double(statistics, "blacklist_miss_ratio", reqs?(((double) ZCSG(blacklist_misses))/reqs)*100.0:0);
624 	add_assoc_double(statistics, "opcache_hit_rate", reqs?(((double) ZCSG(hits))/reqs)*100.0:0);
625 	add_assoc_zval(return_value, "opcache_statistics", statistics);
626 
627 	if (fetch_scripts) {
628 		/* accelerated scripts */
629 		scripts = accelerator_get_scripts(TSRMLS_C);
630 		if (scripts) {
631 			add_assoc_zval(return_value, "scripts", scripts);
632 		}
633 	}
634 }
635 
add_blacklist_path(zend_blacklist_entry * p,zval * return_value TSRMLS_DC)636 static int add_blacklist_path(zend_blacklist_entry *p, zval *return_value TSRMLS_DC)
637 {
638 	add_next_index_stringl(return_value, p->path, p->path_length, 1);
639 	return 0;
640 }
641 
642 /* {{{ proto array accelerator_get_configuration()
643    Obtain configuration information */
ZEND_FUNCTION(opcache_get_configuration)644 static ZEND_FUNCTION(opcache_get_configuration)
645 {
646 	zval *directives,*version,*blacklist;
647 
648 	/* keep the compiler happy */
649 	(void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used;
650 
651 #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
652 	if (zend_parse_parameters_none() == FAILURE) {
653 		RETURN_FALSE;
654 	}
655 #endif
656 
657 	if (!validate_api_restriction(TSRMLS_C)) {
658 		RETURN_FALSE;
659 	}
660 
661 	array_init(return_value);
662 
663 	/* directives */
664 	MAKE_STD_ZVAL(directives);
665 	array_init(directives);
666 	add_assoc_bool(directives, "opcache.enable",              ZCG(enabled));
667 	add_assoc_bool(directives, "opcache.enable_cli",          ZCG(accel_directives).enable_cli);
668 	add_assoc_bool(directives, "opcache.use_cwd",             ZCG(accel_directives).use_cwd);
669 	add_assoc_bool(directives, "opcache.validate_timestamps", ZCG(accel_directives).validate_timestamps);
670 	add_assoc_bool(directives, "opcache.validate_permission", ZCG(accel_directives).validate_permission);
671 #ifndef ZEND_WIN32
672 	add_assoc_bool(directives, "opcache.validate_root",       ZCG(accel_directives).validate_root);
673 #endif
674 	add_assoc_bool(directives, "opcache.inherited_hack",      ZCG(accel_directives).inherited_hack);
675 	add_assoc_bool(directives, "opcache.dups_fix",            ZCG(accel_directives).ignore_dups);
676 	add_assoc_bool(directives, "opcache.revalidate_path",     ZCG(accel_directives).revalidate_path);
677 
678 	add_assoc_long(directives,   "opcache.log_verbosity_level",    ZCG(accel_directives).log_verbosity_level);
679 	add_assoc_long(directives,	 "opcache.memory_consumption",     ZCG(accel_directives).memory_consumption);
680 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
681 	add_assoc_long(directives,	 "opcache.interned_strings_buffer",ZCG(accel_directives).interned_strings_buffer);
682 #endif
683 	add_assoc_long(directives, 	 "opcache.max_accelerated_files",  ZCG(accel_directives).max_accelerated_files);
684 	add_assoc_double(directives, "opcache.max_wasted_percentage",  ZCG(accel_directives).max_wasted_percentage);
685 	add_assoc_long(directives, 	 "opcache.consistency_checks",     ZCG(accel_directives).consistency_checks);
686 	add_assoc_long(directives, 	 "opcache.force_restart_timeout",  ZCG(accel_directives).force_restart_timeout);
687 	add_assoc_long(directives, 	 "opcache.revalidate_freq",        ZCG(accel_directives).revalidate_freq);
688 	add_assoc_string(directives, "opcache.preferred_memory_model", STRING_NOT_NULL(ZCG(accel_directives).memory_model), 1);
689 	add_assoc_string(directives, "opcache.blacklist_filename",     STRING_NOT_NULL(ZCG(accel_directives).user_blacklist_filename), 1);
690 	add_assoc_long(directives,   "opcache.max_file_size",          ZCG(accel_directives).max_file_size);
691 	add_assoc_string(directives, "opcache.error_log",              STRING_NOT_NULL(ZCG(accel_directives).error_log), 1);
692 
693 	add_assoc_bool(directives,   "opcache.protect_memory",         ZCG(accel_directives).protect_memory);
694 	add_assoc_bool(directives,   "opcache.save_comments",          ZCG(accel_directives).save_comments);
695 	add_assoc_bool(directives,   "opcache.load_comments",          ZCG(accel_directives).load_comments);
696 	add_assoc_bool(directives,   "opcache.fast_shutdown",          ZCG(accel_directives).fast_shutdown);
697 	add_assoc_bool(directives,   "opcache.enable_file_override",   ZCG(accel_directives).file_override_enabled);
698 	add_assoc_long(directives, 	 "opcache.optimization_level",     ZCG(accel_directives).optimization_level);
699 
700 	add_assoc_zval(return_value, "directives", directives);
701 
702 	/*version */
703 	MAKE_STD_ZVAL(version);
704 	array_init(version);
705 	add_assoc_string(version, "version", ACCELERATOR_VERSION, 1);
706 	add_assoc_string(version, "opcache_product_name", ACCELERATOR_PRODUCT_NAME, 1);
707 	add_assoc_zval(return_value, "version", version);
708 
709 	/* blacklist */
710 	MAKE_STD_ZVAL(blacklist);
711 	array_init(blacklist);
712 	zend_accel_blacklist_apply(&accel_blacklist, (apply_func_arg_t) add_blacklist_path, blacklist TSRMLS_CC);
713 	add_assoc_zval(return_value, "blacklist", blacklist);
714 }
715 
716 /* {{{ proto void accelerator_reset()
717    Request that the contents of the opcode cache to be reset */
ZEND_FUNCTION(opcache_reset)718 static ZEND_FUNCTION(opcache_reset)
719 {
720 	/* keep the compiler happy */
721 	(void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used;
722 
723 #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
724 	if (zend_parse_parameters_none() == FAILURE) {
725 		RETURN_FALSE;
726 	}
727 #endif
728 
729 	if (!validate_api_restriction(TSRMLS_C)) {
730 		RETURN_FALSE;
731 	}
732 
733 	if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled)) {
734 		RETURN_FALSE;
735 	}
736 
737 	zend_accel_schedule_restart(ACCEL_RESTART_USER TSRMLS_CC);
738 	RETURN_TRUE;
739 }
740 
741 /* {{{ proto void opcache_invalidate(string $script [, bool $force = false])
742    Invalidates cached script (in necessary or forced) */
ZEND_FUNCTION(opcache_invalidate)743 static ZEND_FUNCTION(opcache_invalidate)
744 {
745 	char *script_name;
746 	int script_name_len;
747 	zend_bool force = 0;
748 
749 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &script_name, &script_name_len, &force) == FAILURE) {
750 		return;
751 	}
752 
753 	if (!validate_api_restriction(TSRMLS_C)) {
754 		RETURN_FALSE;
755 	}
756 
757 	if (zend_accel_invalidate(script_name, script_name_len, force TSRMLS_CC) == SUCCESS) {
758 		RETURN_TRUE;
759 	} else {
760 		RETURN_FALSE;
761 	}
762 }
763 
ZEND_FUNCTION(opcache_compile_file)764 static ZEND_FUNCTION(opcache_compile_file)
765 {
766 	char *script_name;
767 	int script_name_len;
768 	zend_file_handle handle;
769 	zend_op_array *op_array = NULL;
770 	zend_execute_data *orig_execute_data = NULL;
771 
772 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &script_name, &script_name_len) == FAILURE) {
773 		return;
774 	}
775 
776 	if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled)) {
777 		zend_error(E_NOTICE, ACCELERATOR_PRODUCT_NAME " seems to be disabled, can't compile file");
778 		RETURN_FALSE;
779 	}
780 
781 	handle.filename = script_name;
782 	handle.free_filename = 0;
783 	handle.opened_path = NULL;
784 	handle.type = ZEND_HANDLE_FILENAME;
785 
786 	orig_execute_data = EG(current_execute_data);
787 
788 	zend_try {
789 		op_array = persistent_compile_file(&handle, ZEND_INCLUDE TSRMLS_CC);
790 	} zend_catch {
791 		EG(current_execute_data) = orig_execute_data;
792 		zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " could not compile file %s", handle.filename);
793 	} zend_end_try();
794 
795 	if(op_array != NULL) {
796 		destroy_op_array(op_array TSRMLS_CC);
797 		efree(op_array);
798 		RETVAL_TRUE;
799 	} else {
800 		RETVAL_FALSE;
801 	}
802 	zend_destroy_file_handle(&handle TSRMLS_CC);
803 }
804 
805 /* {{{ proto bool opcache_is_script_cached(string $script)
806    Return true if the script is cached in OPCache, false if it is not cached or if OPCache is not running. */
ZEND_FUNCTION(opcache_is_script_cached)807 static ZEND_FUNCTION(opcache_is_script_cached)
808 {
809 	char *script_name;
810 	int script_name_len;
811 
812 	if (!validate_api_restriction(TSRMLS_C)) {
813 		RETURN_FALSE;
814 	}
815 
816 	if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled)) {
817 		RETURN_FALSE;
818 	}
819 
820 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &script_name, &script_name_len) == FAILURE) {
821 		return;
822 	}
823 
824 	RETURN_BOOL(filename_is_in_cache(script_name, script_name_len TSRMLS_CC));
825 }
826