diff options
-rw-r--r-- | sapi/litespeed/lsapi_main.c | 415 |
1 files changed, 357 insertions, 58 deletions
diff --git a/sapi/litespeed/lsapi_main.c b/sapi/litespeed/lsapi_main.c index b0ea105a88..65d2451ab6 100644 --- a/sapi/litespeed/lsapi_main.c +++ b/sapi/litespeed/lsapi_main.c @@ -69,6 +69,19 @@ #define SAPI_LSAPI_MAX_HEADER_LENGTH 2048 +/* Key for each cache entry is dirname(PATH_TRANSLATED). + * + * NOTE: Each cache entry config_hash contains the combination from all user ini files found in + * the path starting from doc_root throught to dirname(PATH_TRANSLATED). There is no point + * storing per-file entries as it would not be possible to detect added / deleted entries + * between separate files. + */ +typedef struct _user_config_cache_entry { + time_t expires; + HashTable user_config; +} user_config_cache_entry; +static HashTable user_config_cache; + static int lsapi_mode = 0; static char *php_self = ""; static char *script_filename = ""; @@ -76,6 +89,7 @@ static int source_highlight = 0; static int ignore_php_ini = 0; static char * argv0 = NULL; static int engine = 1; +static int parse_user_ini = 0; #ifdef ZTS zend_compiler_globals *compiler_globals; zend_executor_globals *executor_globals; @@ -86,6 +100,14 @@ void ***tsrm_ls; zend_module_entry litespeed_module_entry; +static void init_sapi_from_env(sapi_module_struct *sapi_module) +{ + char *p; + p = getenv("LSPHPRC"); + if (p) + sapi_module->php_ini_path_override = p; +} + /* {{{ php_lsapi_startup */ static int php_lsapi_startup(sapi_module_struct *sapi_module) @@ -196,46 +218,19 @@ static char *sapi_lsapi_getenv( char * name, size_t name_len ) /* }}} */ - - static int add_variable( const char * pKey, int keyLen, const char * pValue, int valLen, void * arg ) { -#if PHP_MAJOR_VERSION >= 7 - int filter_arg = (Z_ARR_P((zval *)arg) == Z_ARR(PG(http_globals)[TRACK_VARS_ENV])) + int filter_arg = (Z_ARR_P((zval *)arg) == Z_ARR(PG(http_globals)[TRACK_VARS_ENV])) ? PARSE_ENV : PARSE_SERVER; -#else - int filter_arg = (arg == PG(http_globals)[TRACK_VARS_ENV])?PARSE_ENV:PARSE_SERVER; -#endif char * new_val = (char *) pValue; size_t new_val_len; if (sapi_module.input_filter(filter_arg, (char *)pKey, &new_val, valLen, &new_val_len)) { php_register_variable_safe((char *)pKey, new_val, new_val_len, (zval *)arg ); } - return 1; -} - -/* -static int add_variable( const char * pKey, int keyLen, const char * pValue, int valLen, - void * arg ) -{ - zval * gpc_element, **gpc_element_p; - HashTable * symtable1 = Z_ARRVAL_P((zval * )arg); - register char * pKey1 = (char *)pKey; - - MAKE_STD_ZVAL(gpc_element); - Z_STRLEN_P( gpc_element ) = valLen; - Z_STRVAL_P( gpc_element ) = estrndup(pValue, valLen); - Z_TYPE_P( gpc_element ) = IS_STRING; -#if PHP_MAJOR_VERSION > 4 - zend_symtable_update( symtable1, pKey1, keyLen + 1, &gpc_element, sizeof( zval *), (void **) &gpc_element_p ); -#else - zend_hash_update( symtable1, pKey1, keyLen + 1, &gpc_element, sizeof( zval *), (void **) &gpc_element_p ); -#endif return 1; } -*/ static void litespeed_php_import_environment_variables(zval *array_ptr) { @@ -244,7 +239,6 @@ static void litespeed_php_import_environment_variables(zval *array_ptr) size_t alloc_size = sizeof(buf); unsigned long nlen; /* ptrdiff_t is not portable */ -#if PHP_MAJOR_VERSION >= 7 if (Z_TYPE(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY && Z_ARR_P(array_ptr) != Z_ARR(PG(http_globals)[TRACK_VARS_ENV]) && zend_hash_num_elements(Z_ARRVAL(PG(http_globals)[TRACK_VARS_ENV])) > 0 @@ -260,29 +254,6 @@ static void litespeed_php_import_environment_variables(zval *array_ptr) ZVAL_DUP(array_ptr, &PG(http_globals)[TRACK_VARS_SERVER]); return; } -#else - if (PG(http_globals)[TRACK_VARS_ENV] && - array_ptr != PG(http_globals)[TRACK_VARS_ENV] && - Z_TYPE_P(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY && - zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_ENV])) > 0 - ) { - zval_dtor(array_ptr); - *array_ptr = *PG(http_globals)[TRACK_VARS_ENV]; - INIT_PZVAL(array_ptr); - zval_copy_ctor(array_ptr); - return; - } else if (PG(http_globals)[TRACK_VARS_SERVER] && - array_ptr != PG(http_globals)[TRACK_VARS_SERVER] && - Z_TYPE_P(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY && - zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER])) > 0 - ) { - zval_dtor(array_ptr); - *array_ptr = *PG(http_globals)[TRACK_VARS_SERVER]; - INIT_PZVAL(array_ptr); - zval_copy_ctor(array_ptr); - return; - } -#endif for (env = environ; env != NULL && *env != NULL; env++) { p = strchr(*env, '='); @@ -412,13 +383,33 @@ static void sapi_lsapi_log_message(char *message) } /* }}} */ +/* Set to 1 to turn on log messages useful during development: + */ +#if 0 +static void log_message (const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + char buf[0x100]; + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + sapi_lsapi_log_message(buf +#if PHP_MAJOR_VERSION > 7 || (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION >= 1) + , 0 +#endif + ); +} +#define DEBUG_MESSAGE(fmt, ...) log_message("LS:%d " fmt "\n", __LINE__, ##__VA_ARGS__) +#else +#define DEBUG_MESSAGE(fmt, ...) +#endif /* {{{ sapi_module_struct cgi_sapi_module */ static sapi_module_struct lsapi_sapi_module = { "litespeed", - "LiteSpeed V6.9", + "LiteSpeed V6.10", php_lsapi_startup, /* startup */ php_module_shutdown_wrapper, /* shutdown */ @@ -556,6 +547,7 @@ static int lsapi_execute_script( zend_file_handle * file_handle) } +static int lsapi_activate_user_ini(); static int lsapi_module_main(int show_source) { @@ -564,6 +556,11 @@ static int lsapi_module_main(int show_source) if (php_request_startup() == FAILURE ) { return -1; } + + if (parse_user_ini && lsapi_activate_user_ini() == FAILURE) { + return -1; + } + if (show_source) { zend_syntax_highlighter_ini syntax_highlighter_ini; @@ -584,7 +581,7 @@ static int alter_ini( const char * pKey, int keyLen, const char * pValue, int va void * arg ) { #if PHP_MAJOR_VERSION >= 7 - zend_string * psKey; + zend_string * psKey; #endif int type = ZEND_INI_PERDIR; if ( '\001' == *pKey ) { @@ -600,7 +597,7 @@ static int alter_ini( const char * pKey, int keyLen, const char * pValue, int va engine = 0; } else - { + { #if PHP_MAJOR_VERSION >= 7 --keyLen; psKey = zend_string_init(pKey, keyLen, 1); @@ -613,11 +610,293 @@ static int alter_ini( const char * pKey, int keyLen, const char * pValue, int va (char *)pValue, valLen, type, PHP_INI_STAGE_ACTIVATE); #endif - } + } } return 1; } +static void user_config_cache_entry_dtor(zval *el) +{ + user_config_cache_entry *entry = (user_config_cache_entry *)Z_PTR_P(el); + zend_hash_destroy(&entry->user_config); + free(entry); +} + +static void user_config_cache_init() +{ + zend_hash_init(&user_config_cache, 0, NULL, user_config_cache_entry_dtor, 1); +} + +static int pathlen_without_trailing_slash(char *path) +{ + int len = (int)strlen(path); + while (len > 1 && /* mind "/" as root dir */ + path[len-1] == DEFAULT_SLASH) + { + --len; + } + return len; +} + +static inline char* skip_slash(char *s) +{ + while (*s == DEFAULT_SLASH) { + ++s; + } + return s; +} + +/** + * Walk down the path_stop starting at path_start. + * + * If path_start = "/path1" and path_stop = "/path1/path2/path3" + * the callback will be called 3 times with the next args: + * + * 1. "/path1/path2/path3" + * ^ end + * ^ start + * 2. "/path1/path2/path3" + * ^ end + * ^ start + * 3. "/path1/path2/path3" + * ^ end + * ^ start + * + * path_stop has to be a subdir of path_start + * or to be path_start itself. + * + * Both path args have to be absolute. + * Trailing slashes are allowed. + * NULL or empty string args are not allowed. + */ +static void walk_down_the_path(char* path_start, + char* path_stop, + void (*cb)(char* begin, + char* end, + void* data), + void* data) +{ + char *pos = path_stop + pathlen_without_trailing_slash(path_start); + cb(path_stop, pos, data); + + while ((pos = skip_slash(pos))[0]) { + pos = strchr(pos, DEFAULT_SLASH); + if (!pos) { + /* The last token without trailing slash + */ + cb(path_stop, path_stop + strlen(path_stop), data); + return; + } + cb(path_stop, pos, data); + } +} + + +typedef struct { + char *path; + uint path_len; + char *doc_root; + user_config_cache_entry *entry; +} _lsapi_activate_user_ini_ctx; + +typedef int (*fn_activate_user_ini_chain_t) + (_lsapi_activate_user_ini_ctx *ctx, void* next); + +static int lsapi_activate_user_ini_basic_checks(_lsapi_activate_user_ini_ctx *ctx, + void* next) +{ + int rc = SUCCESS; + fn_activate_user_ini_chain_t *fn_next = next; + + if (!PG(user_ini_filename) || !*PG(user_ini_filename)) { + return SUCCESS; + } + + /* PATH_TRANSLATED should be defined at this stage */ + ctx->path = SG(request_info).path_translated; + if (!ctx->path || !*ctx->path) { + return FAILURE; + } + + ctx->doc_root = sapi_lsapi_getenv("DOCUMENT_ROOT", 0); + DEBUG_MESSAGE("doc_root: %s", ctx->doc_root); + + if (*fn_next) { + rc = (*fn_next)(ctx, fn_next + 1); + } + + return rc; +} + +static int lsapi_activate_user_ini_mk_path(_lsapi_activate_user_ini_ctx *ctx, + void* next) +{ + char *path; + int rc = SUCCESS; + fn_activate_user_ini_chain_t *fn_next = next; + + /* Extract dir name from path_translated * and store it in 'path' */ + ctx->path_len = strlen(ctx->path); + path = ctx->path = estrndup(SG(request_info).path_translated, ctx->path_len); + if (!path) + return FAILURE; + ctx->path_len = zend_dirname(path, ctx->path_len); + DEBUG_MESSAGE("dirname: %s", ctx->path); + + if (*fn_next) { + rc = (*fn_next)(ctx, fn_next + 1); + } + + efree(path); + return rc; +} + +static int lsapi_activate_user_ini_mk_realpath(_lsapi_activate_user_ini_ctx *ctx, + void* next) +{ + char *real_path; + int rc = SUCCESS; + fn_activate_user_ini_chain_t *fn_next = next; + + if (!IS_ABSOLUTE_PATH(ctx->path, ctx->path_len)) { + real_path = tsrm_realpath(ctx->path, NULL); + if (!real_path) { + return SUCCESS; + } + ctx->path = real_path; + ctx->path_len = strlen(ctx->path); + DEBUG_MESSAGE("calculated tsrm realpath: %s", real_path); + } else { + DEBUG_MESSAGE("%s is an absolute path", ctx->path); + real_path = NULL; + } + + if (*fn_next) { + rc = (*fn_next)(ctx, fn_next + 1); + } + + if (real_path) + efree(real_path); + return rc; +} + +static int lsapi_activate_user_ini_mk_user_config(_lsapi_activate_user_ini_ctx *ctx, + void* next) +{ + fn_activate_user_ini_chain_t *fn_next = next; + + /* Find cached config entry: If not found, create one */ + ctx->entry = zend_hash_str_find_ptr(&user_config_cache, ctx->path, ctx->path_len); + + if (ctx->entry) { + DEBUG_MESSAGE("found entry for %s", ctx->path); + } else { + DEBUG_MESSAGE("entry for %s not found, creating new entry", ctx->path); + ctx->entry = pemalloc(sizeof(user_config_cache_entry), 1); + ctx->entry->expires = 0; + zend_hash_init(&ctx->entry->user_config, 0, NULL, + config_zval_dtor, 1); + zend_hash_str_update_ptr(&user_config_cache, ctx->path, + ctx->path_len, ctx->entry); + } + + if (*fn_next) { + return (*fn_next)(ctx, fn_next + 1); + } else { + return SUCCESS; + } +} + +static void walk_down_the_path_callback(char* begin, + char* end, + void* data) +{ + _lsapi_activate_user_ini_ctx *ctx = data; + char tmp = end[0]; + end[0] = 0; + DEBUG_MESSAGE("parsing %s%c%s", begin, DEFAULT_SLASH, PG(user_ini_filename)); + php_parse_user_ini_file(begin, PG(user_ini_filename), &ctx->entry->user_config); + end[0] = tmp; +} + +static int lsapi_activate_user_ini_walk_down_the_path(_lsapi_activate_user_ini_ctx *ctx, + void* next) +{ + time_t request_time = sapi_get_request_time(); + uint path_len, docroot_len; + int rc = SUCCESS; + fn_activate_user_ini_chain_t *fn_next = next; + + if (!ctx->entry->expires || request_time > ctx->entry->expires) + { + docroot_len = ctx->doc_root && ctx->doc_root[0] + ? pathlen_without_trailing_slash(ctx->doc_root) + : 0; + + int is_outside_of_docroot = !docroot_len || + ctx->path_len < docroot_len || + strncmp(ctx->path, ctx->doc_root, docroot_len) != 0; + + if (is_outside_of_docroot) { + php_parse_user_ini_file(ctx->path, PG(user_ini_filename), + &ctx->entry->user_config); + } else { + walk_down_the_path(ctx->doc_root, ctx->path, + &walk_down_the_path_callback, ctx); + } + + ctx->entry->expires = request_time + PG(user_ini_cache_ttl); + } + + if (*fn_next) { + rc = (*fn_next)(ctx, fn_next + 1); + } + + return rc; +} + +static int lsapi_activate_user_ini_finally(_lsapi_activate_user_ini_ctx *ctx, + void* next) +{ + int rc = SUCCESS; + fn_activate_user_ini_chain_t *fn_next = next; + + DEBUG_MESSAGE("calling php_ini_activate_config()"); + php_ini_activate_config(&ctx->entry->user_config, PHP_INI_PERDIR, + PHP_INI_STAGE_HTACCESS); + + if (*fn_next) { + rc = (*fn_next)(ctx, fn_next + 1); + } + + return rc; +} + +static int lsapi_activate_user_ini(TSRMLS_D) +{ + _lsapi_activate_user_ini_ctx ctx; + /** + * The reason to have this function list stacked + * is each function now can define a scoped destructor. + * + * Passing control via function pointer is a sign of low coupling, + * which means dependencies between these functions are to be + * controlled from a single place + * (here below, by modifying this function list order) + */ + static const fn_activate_user_ini_chain_t fn_chain[] = { + &lsapi_activate_user_ini_basic_checks, + &lsapi_activate_user_ini_mk_path, + &lsapi_activate_user_ini_mk_realpath, + &lsapi_activate_user_ini_mk_user_config, + &lsapi_activate_user_ini_walk_down_the_path, + &lsapi_activate_user_ini_finally, + NULL + }; + + return fn_chain[0](&ctx, (fn_activate_user_ini_chain_t*)(fn_chain + 1)); +} + static void override_ini() { @@ -811,9 +1090,9 @@ static int cli_main( int argc, char * argv[] ) case 'v': if (php_request_startup() != FAILURE) { #if ZEND_DEBUG - php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2016 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); + php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2015 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); #else - php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2016 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); + php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2015 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); #endif #ifdef PHP_OUTPUT_NEWAPI php_output_end_all(); @@ -1039,6 +1318,11 @@ int main( int argc, char * argv[] ) lsapi_sapi_module.executable_location = argv[0]; + /* Initialize from environment variables before processing command-line + * options: the latter override the former. + */ + init_sapi_from_env(&lsapi_sapi_module); + if ( ignore_php_ini ) lsapi_sapi_module.php_ini_ignore = 1; @@ -1121,17 +1405,24 @@ int main( int argc, char * argv[] ) /* LiteSpeed PHP module starts here */ +#if PHP_MAJOR_VERSION > 4 + /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO(arginfo_litespeed__void, 0) ZEND_END_ARG_INFO() /* }}} */ +#else +#define arginfo_litespeed__void NULL +#endif + PHP_FUNCTION(litespeed_request_headers); PHP_FUNCTION(litespeed_response_headers); PHP_FUNCTION(apache_get_modules); PHP_MINFO_FUNCTION(litespeed); + zend_function_entry litespeed_functions[] = { PHP_FE(litespeed_request_headers, arginfo_litespeed__void) PHP_FE(litespeed_response_headers, arginfo_litespeed__void) @@ -1144,6 +1435,12 @@ zend_function_entry litespeed_functions[] = { static PHP_MINIT_FUNCTION(litespeed) { + user_config_cache_init(); + + const char *p = getenv("LSPHP_ENABLE_USER_INI"); + if (p && 0 == strcasecmp(p, "on")) + parse_user_ini = 1; + /* REGISTER_INI_ENTRIES(); */ return SUCCESS; } @@ -1151,6 +1448,8 @@ static PHP_MINIT_FUNCTION(litespeed) static PHP_MSHUTDOWN_FUNCTION(litespeed) { + zend_hash_destroy(&user_config_cache); + /* UNREGISTER_INI_ENTRIES(); */ return SUCCESS; } |