diff options
Diffstat (limited to 'sapi/phpdbg/phpdbg.c')
-rw-r--r-- | sapi/phpdbg/phpdbg.c | 1348 |
1 files changed, 1348 insertions, 0 deletions
diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c new file mode 100644 index 0000000000..e7f841c0bc --- /dev/null +++ b/sapi/phpdbg/phpdbg.c @@ -0,0 +1,1348 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_SIGNALS +# include <signal.h> +#endif +#include "phpdbg.h" +#include "phpdbg_prompt.h" +#include "phpdbg_bp.h" +#include "phpdbg_break.h" +#include "phpdbg_list.h" +#include "phpdbg_utils.h" +#include "phpdbg_set.h" + +/* {{{ remote console headers */ +#ifndef _WIN32 +# include <sys/socket.h> +# include <sys/select.h> +# include <sys/time.h> +# include <sys/types.h> +# include <unistd.h> +# include <arpa/inet.h> +#endif /* }}} */ + +ZEND_DECLARE_MODULE_GLOBALS(phpdbg); + +static zend_bool phpdbg_booted = 0; + +#if PHP_VERSION_ID >= 50500 +void (*zend_execute_old)(zend_execute_data *execute_data TSRMLS_DC); +#else +void (*zend_execute_old)(zend_op_array *op_array TSRMLS_DC); +#endif + +static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */ +{ + pg->prompt[0] = NULL; + pg->prompt[1] = NULL; + + pg->colors[0] = NULL; + pg->colors[1] = NULL; + pg->colors[2] = NULL; + + pg->exec = NULL; + pg->exec_len = 0; + pg->ops = NULL; + pg->vmret = 0; + pg->bp_count = 0; + pg->lcmd = NULL; + pg->flags = PHPDBG_DEFAULT_FLAGS; + pg->oplog = NULL; + pg->io[PHPDBG_STDIN] = NULL; + pg->io[PHPDBG_STDOUT] = NULL; + pg->io[PHPDBG_STDERR] = NULL; + memset(&pg->lparam, 0, sizeof(phpdbg_param_t)); + pg->frame.num = 0; +} /* }}} */ + +static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ +{ + ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL); +#if PHP_VERSION_ID >= 50500 + zend_execute_old = zend_execute_ex; + zend_execute_ex = phpdbg_execute_ex; +#else + zend_execute_old = zend_execute; + zend_execute = phpdbg_execute_ex; +#endif + + REGISTER_STRINGL_CONSTANT("PHPDBG_VERSION", PHPDBG_VERSION, sizeof(PHPDBG_VERSION)-1, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("PHPDBG_FILE", FILE_PARAM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_METHOD", METHOD_PARAM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_LINENO", NUMERIC_PARAM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_FUNC", STR_PARAM, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("PHPDBG_COLOR_PROMPT", PHPDBG_COLOR_PROMPT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_COLOR_NOTICE", PHPDBG_COLOR_NOTICE, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_COLOR_ERROR", PHPDBG_COLOR_ERROR, CONST_CS|CONST_PERSISTENT); + + return SUCCESS; +} /* }}} */ + +static void php_phpdbg_destroy_bp_file(void *brake) /* {{{ */ +{ + zend_hash_destroy((HashTable*)brake); +} /* }}} */ + +static void php_phpdbg_destroy_bp_symbol(void *brake) /* {{{ */ +{ + efree((char*)((phpdbg_breaksymbol_t*)brake)->symbol); +} /* }}} */ + +static void php_phpdbg_destroy_bp_opcode(void *brake) /* {{{ */ +{ + efree((char*)((phpdbg_breakop_t*)brake)->name); +} /* }}} */ + + +static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */ +{ + zend_hash_destroy((HashTable*)brake); +} /* }}} */ + +static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */ +{ + phpdbg_breakcond_t *brake = (phpdbg_breakcond_t*) data; + + if (brake) { + if (brake->ops) { + TSRMLS_FETCH(); + + destroy_op_array( + brake->ops TSRMLS_CC); + efree(brake->ops); + } + efree((char*)brake->code); + } +} /* }}} */ + +static void php_phpdbg_destroy_registered(void *data) /* {{{ */ +{ + zend_function *function = (zend_function*) data; + TSRMLS_FETCH(); + + destroy_zend_function( + function TSRMLS_CC); +} /* }}} */ + +static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */ +{ + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0); + + zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0); + zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0); + + return SUCCESS; +} /* }}} */ + +static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ +{ + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]); + zend_hash_destroy(&PHPDBG_G(seek)); + zend_hash_destroy(&PHPDBG_G(registered)); + + if (PHPDBG_G(exec)) { + efree(PHPDBG_G(exec)); + PHPDBG_G(exec) = NULL; + } + + if (PHPDBG_G(prompt)[0]) { + free(PHPDBG_G(prompt)[0]); + } + if (PHPDBG_G(prompt)[1]) { + free(PHPDBG_G(prompt)[1]); + } + + PHPDBG_G(prompt)[0] = NULL; + PHPDBG_G(prompt)[1] = NULL; + + if (PHPDBG_G(oplog)) { + fclose( + PHPDBG_G(oplog)); + PHPDBG_G(oplog) = NULL; + } + + if (PHPDBG_G(ops)) { + destroy_op_array(PHPDBG_G(ops) TSRMLS_CC); + efree(PHPDBG_G(ops)); + PHPDBG_G(ops) = NULL; + } + + return SUCCESS; +} /* }}} */ + +/* {{{ proto mixed phpdbg_exec(string context) + Attempt to set the execution context for phpdbg + If the execution context was set previously it is returned + If the execution context was not set previously boolean true is returned + If the request to set the context fails, boolean false is returned, and an E_WARNING raised */ +static PHP_FUNCTION(phpdbg_exec) +{ + char *exec = NULL; + zend_ulong exec_len = 0L; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) { + return; + } + + { + struct stat sb; + zend_bool result = 1; + + if (VCWD_STAT(exec, &sb) != FAILURE) { + if (sb.st_mode & (S_IFREG|S_IFLNK)) { + if (PHPDBG_G(exec)) { + ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len), 1); + efree(PHPDBG_G(exec)); + result = 0; + } + + PHPDBG_G(exec) = estrndup(exec, exec_len); + PHPDBG_G(exec_len) = exec_len; + + if (result) + ZVAL_BOOL(return_value, 1); + } else { + zend_error( + E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", exec); + ZVAL_BOOL(return_value, 0); + } + } else { + zend_error( + E_WARNING, "Failed to set execution context (%s) the file does not exist", exec); + + ZVAL_BOOL(return_value, 0); + } + } +} /* }}} */ + +/* {{{ proto void phpdbg_break([integer type, string expression]) + instructs phpdbg to insert a breakpoint at the next opcode */ +static PHP_FUNCTION(phpdbg_break) +{ + if (ZEND_NUM_ARGS() > 0) { + long type; + char *expr = NULL; + zend_uint expr_len = 0; + phpdbg_param_t param; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &type, &expr, &expr_len) == FAILURE) { + return; + } + + phpdbg_parse_param(expr, expr_len, ¶m TSRMLS_CC); + + switch (type) { + case METHOD_PARAM: + phpdbg_do_break_method(¶m, NULL TSRMLS_CC); + break; + + case FILE_PARAM: + phpdbg_do_break_file(¶m, NULL TSRMLS_CC); + break; + + case NUMERIC_PARAM: + phpdbg_do_break_lineno(¶m, NULL TSRMLS_CC); + break; + + case STR_PARAM: + phpdbg_do_break_func(¶m, NULL TSRMLS_CC); + break; + + default: zend_error( + E_WARNING, "unrecognized parameter type %ld", type); + } + + phpdbg_clear_param(¶m TSRMLS_CC); + + } else if (EG(current_execute_data) && EG(active_op_array)) { + zend_ulong opline_num = (EG(current_execute_data)->opline - + EG(active_op_array)->opcodes); + + phpdbg_set_breakpoint_opline_ex( + &EG(active_op_array)->opcodes[opline_num+1] TSRMLS_CC); + } +} /* }}} */ + +/* {{{ proto void phpdbg_clear(void) + instructs phpdbg to clear breakpoints */ +static PHP_FUNCTION(phpdbg_clear) +{ + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); +} /* }}} */ + +/* {{{ proto void phpdbg_color(integer element, string color) */ +static PHP_FUNCTION(phpdbg_color) +{ + long element; + char *color; + zend_uint color_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) { + return; + } + + switch (element) { + case PHPDBG_COLOR_NOTICE: + case PHPDBG_COLOR_ERROR: + case PHPDBG_COLOR_PROMPT: + phpdbg_set_color_ex(element, color, color_len TSRMLS_CC); + break; + + default: zend_error(E_ERROR, "phpdbg detected an incorrect color constant"); + } +} /* }}} */ + +/* {{{ proto void phpdbg_prompt(string prompt) */ +static PHP_FUNCTION(phpdbg_prompt) +{ + char *prompt; + zend_uint prompt_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) { + return; + } + + phpdbg_set_prompt(prompt TSRMLS_CC); +} /* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, expression) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_color_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, element) + ZEND_ARG_INFO(0, color) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_prompt_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, string) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_exec_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, context) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_clear_arginfo, 0, 0, 0) +ZEND_END_ARG_INFO() + +zend_function_entry phpdbg_user_functions[] = { + PHP_FE(phpdbg_clear, phpdbg_clear_arginfo) + PHP_FE(phpdbg_break, phpdbg_break_arginfo) + PHP_FE(phpdbg_exec, phpdbg_exec_arginfo) + PHP_FE(phpdbg_color, phpdbg_color_arginfo) + PHP_FE(phpdbg_prompt, phpdbg_prompt_arginfo) +#ifdef PHP_FE_END + PHP_FE_END +#else + {NULL,NULL,NULL} +#endif +}; + +static zend_module_entry sapi_phpdbg_module_entry = { + STANDARD_MODULE_HEADER, + PHPDBG_NAME, + phpdbg_user_functions, + PHP_MINIT(phpdbg), + NULL, + PHP_RINIT(phpdbg), + PHP_RSHUTDOWN(phpdbg), + NULL, + PHPDBG_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */ +{ + if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) { + return FAILURE; + } + + phpdbg_booted=1; + + return SUCCESS; +} /* }}} */ + +static char* php_sapi_phpdbg_read_cookies(TSRMLS_D) /* {{{ */ +{ + return NULL; +} /* }}} */ + +static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s TSRMLS_DC) /* {{{ */ +{ + return 0; +} +/* }}} */ + +static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */ +{ + /* We do nothing here, this function is needed to prevent that the fallback + * header handling is called. */ + return SAPI_HEADER_SENT_SUCCESSFULLY; +} +/* }}} */ + +static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC) /* {{{ */ +{ +} +/* }}} */ + +static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */ +{ + /* + * We must not request TSRM before being boot + */ + if (phpdbg_booted) { + phpdbg_error("%s", message); + + switch (PG(last_error_type)) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + case E_PARSE: + case E_RECOVERABLE_ERROR: + if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { + phpdbg_list_file( + zend_get_executed_filename(TSRMLS_C), + 3, + zend_get_executed_lineno(TSRMLS_C)-1, + zend_get_executed_lineno(TSRMLS_C) + TSRMLS_CC + ); + } + + do { + switch (phpdbg_interactive(TSRMLS_C)) { + case PHPDBG_LEAVE: + case PHPDBG_FINISH: + case PHPDBG_UNTIL: + case PHPDBG_NEXT: + return; + } + } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); + + } + } else fprintf(stdout, "%s\n", message); +} +/* }}} */ + +static int php_sapi_phpdbg_deactivate(TSRMLS_D) /* {{{ */ +{ + fflush(stdout); + if(SG(request_info).argv0) { + free(SG(request_info).argv0); + SG(request_info).argv0 = NULL; + } + return SUCCESS; +} +/* }}} */ + +static void php_sapi_phpdbg_register_vars(zval *track_vars_array TSRMLS_DC) /* {{{ */ +{ + unsigned int len; + char *docroot = ""; + + /* In phpdbg mode, we consider the environment to be a part of the server variables + */ + php_import_environment_variables(track_vars_array TSRMLS_CC); + + if (PHPDBG_G(exec)) { + len = PHPDBG_G(exec_len); + if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("PHP_SELF", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("SCRIPT_NAME", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + + if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + } + + /* any old docroot will doo */ + len = 0U; + if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", + &docroot, len, &len TSRMLS_CC)) { + php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array TSRMLS_CC); + } +} +/* }}} */ + +static inline int php_sapi_phpdbg_ub_write(const char *message, unsigned int length TSRMLS_DC) /* {{{ */ +{ + return phpdbg_write("%s", message); +} /* }}} */ + +#if PHP_VERSION_ID >= 50700 +static inline void php_sapi_phpdbg_flush(void *context TSRMLS_DC) /* {{{ */ +{ +#else +static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */ +{ + TSRMLS_FETCH(); +#endif + + fflush(PHPDBG_G(io)[PHPDBG_STDOUT]); +} /* }}} */ + +/* {{{ sapi_module_struct phpdbg_sapi_module +*/ +static sapi_module_struct phpdbg_sapi_module = { + "phpdbg", /* name */ + "phpdbg", /* pretty name */ + + php_sapi_phpdbg_module_startup, /* startup */ + php_module_shutdown_wrapper, /* shutdown */ + + NULL, /* activate */ + php_sapi_phpdbg_deactivate, /* deactivate */ + + php_sapi_phpdbg_ub_write, /* unbuffered write */ + php_sapi_phpdbg_flush, /* flush */ + NULL, /* get uid */ + NULL, /* getenv */ + + php_error, /* error handler */ + + php_sapi_phpdbg_header_handler, /* header handler */ + php_sapi_phpdbg_send_headers, /* send headers handler */ + php_sapi_phpdbg_send_header, /* send header handler */ + + NULL, /* read POST data */ + php_sapi_phpdbg_read_cookies, /* read Cookies */ + + php_sapi_phpdbg_register_vars, /* register server variables */ + php_sapi_phpdbg_log_message, /* Log message */ + NULL, /* Get request time */ + NULL, /* Child terminate */ + STANDARD_SAPI_MODULE_PROPERTIES +}; +/* }}} */ + +const opt_struct OPTIONS[] = { /* {{{ */ + {'c', 1, "ini path override"}, + {'d', 1, "define ini entry on command line"}, + {'n', 0, "no php.ini"}, + {'z', 1, "load zend_extension"}, + /* phpdbg options */ + {'q', 0, "no banner"}, + {'e', 1, "exec"}, + {'v', 0, "disable quietness"}, + {'s', 0, "enable stepping"}, + {'b', 0, "boring colours"}, + {'i', 1, "specify init"}, + {'I', 0, "ignore init"}, + {'O', 1, "opline log"}, + {'r', 0, "run"}, + {'E', 0, "step-through-eval"}, + {'S', 1, "sapi-name"}, +#ifndef _WIN32 + {'l', 1, "listen"}, + {'a', 1, "address-or-any"}, +#endif + {'V', 0, "version"}, + {'-', 0, NULL} +}; /* }}} */ + +const char phpdbg_ini_hardcoded[] = +"html_errors=Off\n" +"register_argc_argv=On\n" +"implicit_flush=On\n" +"display_errors=Off\n" +"log_errors=On\n" +"max_execution_time=0\n" +"max_input_time=-1\n" +"error_log=\n\0"; + +/* overwriteable ini defaults must be set in phpdbg_ini_defaults() */ +#define INI_DEFAULT(name, value) \ + Z_SET_REFCOUNT(tmp, 0); \ + Z_UNSET_ISREF(tmp); \ + ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \ + zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL); + +void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */ +{ + zval tmp; + INI_DEFAULT("report_zend_debug", "0"); +} /* }}} */ + +static void phpdbg_welcome(zend_bool cleaning TSRMLS_DC) /* {{{ */ +{ + /* print blurb */ + if (!cleaning) { + phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", + PHPDBG_VERSION); + phpdbg_writeln("To get help using phpdbg type \"help\" and press enter"); + phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES); + } else { + phpdbg_notice("Clean Execution Environment"); + + phpdbg_writeln("Classes\t\t\t%d", zend_hash_num_elements(EG(class_table))); + phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(EG(function_table))); + phpdbg_writeln("Constants\t\t%d", zend_hash_num_elements(EG(zend_constants))); + phpdbg_writeln("Includes\t\t%d", zend_hash_num_elements(&EG(included_files))); + } +} /* }}} */ + +static inline void phpdbg_sigint_handler(int signo) /* {{{ */ +{ + TSRMLS_FETCH(); + + if (EG(in_execution)) { + /* set signalled only when not interactive */ + if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) { + PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED; + } + } else { + /* we quit remote consoles on recv SIGINT */ + if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { + PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; + zend_bailout(); + } + } +} /* }}} */ + +#ifndef _WIN32 +int phpdbg_open_socket(const char *interface, short port) /* {{{ */ +{ + int fd = socket(AF_INET, SOCK_STREAM, 0); + + switch (fd) { + case -1: + return -1; + + default: { + int reuse = 1; + + switch (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse))) { + case -1: + close(fd); + return -2; + + default: { + struct sockaddr_in address; + + memset(&address, 0, sizeof(address)); + + address.sin_port = htons(port); + address.sin_family = AF_INET; + + if ((*interface == '*')) { + address.sin_addr.s_addr = htonl(INADDR_ANY); + } else if (!inet_pton(AF_INET, interface, &address.sin_addr)) { + close(fd); + return -3; + } + + switch (bind(fd, (struct sockaddr *)&address, sizeof(address))) { + case -1: + close(fd); + return -4; + + default: { + listen(fd, 5); + } + } + } + } + } + } + + return fd; +} /* }}} */ + +static inline void phpdbg_close_sockets(int (*socket)[2], FILE *streams[2]) /* {{{ */ +{ + if ((*socket)[0] >= 0) { + shutdown( + (*socket)[0], SHUT_RDWR); + close((*socket)[0]); + } + + if (streams[0]) { + fclose(streams[0]); + } + + if ((*socket)[1] >= 0) { + shutdown( + (*socket)[1], SHUT_RDWR); + close((*socket)[1]); + } + + if (streams[1]) { + fclose(streams[1]); + } +} /* }}} */ + +/* don't inline this, want to debug it easily, will inline when done */ + +int phpdbg_open_sockets(char *address, int port[2], int (*listen)[2], int (*socket)[2], FILE* streams[2]) /* {{{ */ +{ + if (((*listen)[0]) < 0 && ((*listen)[1]) < 0) { + ((*listen)[0]) = phpdbg_open_socket(address, (short)port[0]); + ((*listen)[1]) = phpdbg_open_socket(address, (short)port[1]); + } + + streams[0] = NULL; + streams[1] = NULL; + + if ((*listen)[0] < 0 || (*listen)[1] < 0) { + if ((*listen)[0] < 0) { + phpdbg_rlog(stderr, + "console failed to initialize (stdin) on %s:%d", address, port[0]); + } + + if ((*listen)[1] < 0) { + phpdbg_rlog(stderr, + "console failed to initialize (stdout) on %s:%d", address, port[1]); + } + + if ((*listen)[0] >= 0) { + close((*listen)[0]); + } + + if ((*listen)[1] >= 0) { + close((*listen)[1]); + } + + return FAILURE; + } + + phpdbg_close_sockets(socket, streams); + + phpdbg_rlog(stderr, + "accepting connections on %s:%d/%d", address, port[0], port[1]); + { + struct sockaddr_in address; + socklen_t size = sizeof(address); + char buffer[20] = {0}; + + { + memset(&address, 0, size); + (*socket)[0] = accept( + (*listen)[0], (struct sockaddr *) &address, &size); + inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer)); + + phpdbg_rlog(stderr, "connection (stdin) from %s", buffer); + } + + { + memset(&address, 0, size); + (*socket)[1] = accept( + (*listen)[1], (struct sockaddr *) &address, &size); + inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer)); + + phpdbg_rlog(stderr, "connection (stdout) from %s", buffer); + } + } + + dup2((*socket)[0], fileno(stdin)); + dup2((*socket)[1], fileno(stdout)); + + setbuf(stdout, NULL); + + streams[0] = fdopen((*socket)[0], "r"); + streams[1] = fdopen((*socket)[1], "w"); + + return SUCCESS; +} /* }}} */ +#endif + +int main(int argc, char **argv) /* {{{ */ +{ + sapi_module_struct *phpdbg = &phpdbg_sapi_module; + char *sapi_name; + char *ini_entries; + int ini_entries_len; + char **zend_extensions = NULL; + zend_ulong zend_extensions_len = 0L; + zend_bool ini_ignore; + char *ini_override; + char *exec; + size_t exec_len; + char *init_file; + size_t init_file_len; + zend_bool init_file_default; + char *oplog_file; + size_t oplog_file_len; + zend_ulong flags; + char *php_optarg; + int php_optind, opt, show_banner = 1; + long cleaning = 0; + zend_bool remote = 0; + int run = 0; + int step = 0; + char *bp_tmp_file; +#ifndef _WIN32 + char *address; + int listen[2]; + int server[2]; + int socket[2]; + FILE* streams[2] = {NULL, NULL}; +#endif + +#ifdef ZTS + void ***tsrm_ls; +#endif + +#ifndef _WIN32 + address = strdup("127.0.0.1"); + socket[0] = -1; + socket[1] = -1; + listen[0] = -1; + listen[1] = -1; + server[0] = -1; + server[1] = -1; + streams[0] = NULL; + streams[1] = NULL; +#endif + +#ifdef PHP_WIN32 + _fmode = _O_BINARY; /* sets default for file streams to binary */ + setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */ + setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */ + setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */ +#endif + +#ifdef ZTS + tsrm_startup(1, 1, 0, NULL); + + tsrm_ls = ts_resource(0); +#endif + +phpdbg_main: + if (!cleaning) { + bp_tmp_file = malloc(L_tmpnam); + tmpnam(bp_tmp_file); + if (bp_tmp_file == NULL) { + phpdbg_error("Unable to create temporary file"); + } + } + ini_entries = NULL; + ini_entries_len = 0; + ini_ignore = 0; + ini_override = NULL; + zend_extensions = NULL; + zend_extensions_len = 0L; + exec = NULL; + exec_len = 0; + init_file = NULL; + init_file_len = 0; + init_file_default = 1; + oplog_file = NULL; + oplog_file_len = 0; + flags = PHPDBG_DEFAULT_FLAGS; + php_optarg = NULL; + php_optind = 1; + opt = 0; + run = 0; + step = 0; + sapi_name = NULL; + + + while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { + switch (opt) { + case 'r': + run++; + break; + case 'n': + ini_ignore = 1; + break; + case 'c': + if (ini_override) { + free(ini_override); + } + ini_override = strdup(php_optarg); + break; + case 'd': { + int len = strlen(php_optarg); + char *val; + + if ((val = strchr(php_optarg, '='))) { + val++; + if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg)); + ini_entries_len += (val - php_optarg); + memcpy(ini_entries + ini_entries_len, "\"", 1); + ini_entries_len++; + memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg)); + ini_entries_len += len - (val - php_optarg); + memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0")); + ini_entries_len += sizeof("\n\0\"") - 2; + } else { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, len); + memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0")); + ini_entries_len += len + sizeof("\n\0") - 2; + } + } else { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, len); + memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0")); + ini_entries_len += len + sizeof("=1\n\0") - 2; + } + } break; + + case 'z': + zend_extensions_len++; + if (zend_extensions) { + zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len); + } else zend_extensions = malloc(sizeof(char*) * zend_extensions_len); + zend_extensions[zend_extensions_len-1] = strdup(php_optarg); + break; + + /* begin phpdbg options */ + + case 'e': { /* set execution context */ + exec_len = strlen(php_optarg); + if (exec_len) { + if (exec) { + free(exec); + } + exec = strdup(php_optarg); + } + } break; + + case 'S': { /* set SAPI name */ + if (sapi_name) { + free(sapi_name); + } + sapi_name = strdup(php_optarg); + } break; + + case 'I': { /* ignore .phpdbginit */ + init_file_default = 0; + } break; + + case 'i': { /* set init file */ + if (init_file) { + free(init_file); + } + + init_file_len = strlen(php_optarg); + if (init_file_len) { + init_file = strdup(php_optarg); + } + } break; + + case 'O': { /* set oplog output */ + oplog_file_len = strlen(php_optarg); + if (oplog_file_len) { + oplog_file = strdup(php_optarg); + } + } break; + + case 'v': /* set quietness off */ + flags &= ~PHPDBG_IS_QUIET; + break; + + case 's': /* set stepping on */ + step = 1; + break; + + case 'E': /* stepping through eval on */ + flags |= PHPDBG_IS_STEPONEVAL; + break; + + case 'b': /* set colours off */ + flags &= ~PHPDBG_IS_COLOURED; + break; + + case 'q': /* hide banner */ + show_banner = 0; + break; + +#ifndef _WIN32 + /* if you pass a listen port, we will accept input on listen port */ + /* and write output to listen port * 2 */ + + case 'l': { /* set listen ports */ + if (sscanf(php_optarg, "%d/%d", &listen[0], &listen[1]) != 2) { + if (sscanf(php_optarg, "%d", &listen[0]) != 1) { + /* default to hardcoded ports */ + listen[0] = 4000; + listen[1] = 8000; + } else { + listen[1] = (listen[0] * 2); + } + } + } break; + + case 'a': { /* set bind address */ + free(address); + if (!php_optarg) { + address = strdup("*"); + } else address = strdup(php_optarg); + } break; +#endif + + case 'V': { + sapi_startup(phpdbg); + phpdbg->startup(phpdbg); + printf( + "phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) 1997-2014 The PHP Group\n%s", + PHPDBG_VERSION, + __DATE__, + __TIME__, + PHP_VERSION, + get_zend_version() + ); + sapi_deactivate(TSRMLS_C); + sapi_shutdown(); + return 0; + } break; + } + } + +#ifndef _WIN32 + /* setup remote server if necessary */ + if (!cleaning && + (listen[0] > 0 && listen[1] > 0)) { + if (phpdbg_open_sockets(address, listen, &server, &socket, streams) == FAILURE) { + remote = 0; + exit(0); + } + /* set remote flag to stop service shutting down upon quit */ + remote = 1; + } +#endif + + if (sapi_name) { + phpdbg->name = sapi_name; + } + + phpdbg->ini_defaults = phpdbg_ini_defaults; + phpdbg->phpinfo_as_text = 1; + phpdbg->php_ini_ignore_cwd = 1; + + sapi_startup(phpdbg); + + phpdbg->executable_location = argv[0]; + phpdbg->phpinfo_as_text = 1; + phpdbg->php_ini_ignore = ini_ignore; + phpdbg->php_ini_path_override = ini_override; + + if (ini_entries) { + ini_entries = realloc(ini_entries, ini_entries_len + sizeof(phpdbg_ini_hardcoded)); + memmove(ini_entries + sizeof(phpdbg_ini_hardcoded) - 2, ini_entries, ini_entries_len + 1); + memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded) - 2); + } else { + ini_entries = malloc(sizeof(phpdbg_ini_hardcoded)); + memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded)); + } + ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2; + + if (zend_extensions_len) { + zend_ulong zend_extension = 0L; + + while (zend_extension < zend_extensions_len) { + const char *ze = zend_extensions[zend_extension]; + size_t ze_len = strlen(ze); + + ini_entries = realloc( + ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n")))); + memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1)); + ini_entries_len += (sizeof("zend_extension=")-1); + memcpy(&ini_entries[ini_entries_len], ze, ze_len); + ini_entries_len += ze_len; + memcpy(&ini_entries[ini_entries_len], "\n", (sizeof("\n") - 1)); + + free(zend_extensions[zend_extension]); + zend_extension++; + } + + free(zend_extensions); + } + + phpdbg->ini_entries = ini_entries; + + if (phpdbg->startup(phpdbg) == SUCCESS) { + + zend_activate(TSRMLS_C); + + /* do not install sigint handlers for remote consoles */ + /* sending SIGINT then provides a decent way of shutting down the server */ +#ifdef ZEND_SIGNALS +# ifndef _WIN32 + if (listen[0] < 0) { +# endif + zend_try { + zend_signal_activate(TSRMLS_C); + zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); + } zend_end_try(); +# ifndef _WIN32 + } +# endif +#else +# ifndef _WIN32 + if (listen[0] < 0) { +# endif + signal(SIGINT, phpdbg_sigint_handler); +#ifndef _WIN32 + } +#endif +#endif + + PG(modules_activated) = 0; + + /* set flags from command line */ + PHPDBG_G(flags) = flags; + +#ifndef _WIN32 + /* setup io here */ + if (streams[0] && streams[1]) { + PHPDBG_G(flags) |= PHPDBG_IS_REMOTE; + + signal(SIGPIPE, SIG_IGN); + } +#endif + + PHPDBG_G(io)[PHPDBG_STDIN] = stdin; + PHPDBG_G(io)[PHPDBG_STDOUT] = stdout; + PHPDBG_G(io)[PHPDBG_STDERR] = stderr; + + if (exec) { /* set execution context */ + PHPDBG_G(exec) = phpdbg_resolve_path( + exec TSRMLS_CC); + PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec)); + + free(exec); + } + + if (oplog_file) { /* open oplog */ + PHPDBG_G(oplog) = fopen(oplog_file, "w+"); + if (!PHPDBG_G(oplog)) { + phpdbg_error( + "Failed to open oplog %s", oplog_file); + } + free(oplog_file); + } + + /* set default colors */ + phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold") TSRMLS_CC); + phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold") TSRMLS_CC); + phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green") TSRMLS_CC); + + /* set default prompt */ + phpdbg_set_prompt(PROMPT TSRMLS_CC); + + zend_try { + zend_activate_modules(TSRMLS_C); + } zend_end_try(); + + if (show_banner) { + /* print blurb */ + phpdbg_welcome((cleaning > 0) TSRMLS_CC); + } + + zend_try { + /* activate globals, they can be overwritten */ + zend_activate_auto_globals(TSRMLS_C); + } zend_end_try(); + + /* initialize from file */ + PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING; + zend_try { + phpdbg_init(init_file, init_file_len, init_file_default TSRMLS_CC); + phpdbg_try_file_init(bp_tmp_file, strlen(bp_tmp_file), 0 TSRMLS_CC); + } zend_end_try(); + PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING; + + /* quit if init says so */ + if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) { + goto phpdbg_out; + } + + /* step from here, not through init */ + if (step) { + PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; + } + + if (run) { + /* no need to try{}, run does it ... */ + PHPDBG_COMMAND_HANDLER(run)(NULL, NULL TSRMLS_CC); + if (run > 1) { + /* if -r is on the command line more than once just quit */ + goto phpdbg_out; + } + } + +phpdbg_interact: + /* phpdbg main() */ + do { + zend_try { + phpdbg_interactive(TSRMLS_C); + } zend_catch { + if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) { + FILE *bp_tmp_fp = fopen(bp_tmp_file, "w"); + phpdbg_export_breakpoints(bp_tmp_fp TSRMLS_CC); + fclose(bp_tmp_fp); + cleaning = 1; + } else { + cleaning = 0; + } + +#ifndef _WIN32 + if (!cleaning) { + /* remote client disconnected */ + if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { + + if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { + /* renegociate connections */ + phpdbg_open_sockets( + address, listen, &server, &socket, streams); + + /* set streams */ + if (streams[0] && streams[1]) { + PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING; + } + + /* this must be forced */ + CG(unclean_shutdown) = 0; + } else { + /* local consoles cannot disconnect, ignore EOF */ + PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED; + } + } + } +#endif + } zend_end_try(); + } while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); + + /* this must be forced */ + CG(unclean_shutdown) = 0; + + /* this is just helpful */ + PG(report_memleaks) = 0; + +phpdbg_out: +#ifndef _WIN32 + if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { + PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED; + goto phpdbg_interact; + } +#endif + +#ifndef ZTS + /* force cleanup of auto and core globals */ + zend_hash_clean(CG(auto_globals)); + memset( + &core_globals, 0, sizeof(php_core_globals)); +#endif + + if (ini_entries) { + free(ini_entries); + } + + if (ini_override) { + free(ini_override); + } + + if (PG(modules_activated)) { + zend_try { + zend_deactivate_modules(TSRMLS_C); + } zend_end_try(); + } + + zend_deactivate(TSRMLS_C); + + zend_try { + zend_post_deactivate_modules(TSRMLS_C); + } zend_end_try(); + +#ifdef ZEND_SIGNALS + zend_try { + zend_signal_deactivate(TSRMLS_C); + } zend_end_try(); +#endif + + zend_try { + php_module_shutdown(TSRMLS_C); + } zend_end_try(); + + sapi_shutdown(); + } + + if (cleaning || remote) { + goto phpdbg_main; + } + +#ifdef ZTS + /* bugggy */ + /* tsrm_shutdown(); */ +#endif + +#ifndef _WIN32 + if (address) { + free(address); + } +#endif + + if (sapi_name) { + free(sapi_name); + } + + free(bp_tmp_file); + + return 0; +} /* }}} */ |