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