diff options
author | Rasmus Lerdorf <rasmus@php.net> | 2014-10-26 16:58:45 -0700 |
---|---|---|
committer | Rasmus Lerdorf <rasmus@php.net> | 2014-10-26 16:58:45 -0700 |
commit | 8d84b1f67adb71bdb2394b62faad04b22f6226f3 (patch) | |
tree | 0c1c1ed2aaef8eee971575eb5e336631295f4951 /sapi/phpdbg/phpdbg.c | |
parent | fb85d0322d39d49f37e32df6f68c9769f2cce0e4 (diff) | |
parent | 09da8952d0434b53e740ffaca66a8051cd39cf93 (diff) | |
download | php-git-8d84b1f67adb71bdb2394b62faad04b22f6226f3.tar.gz |
Merge branch 'master' of git.php.net:php-src
* 'master' of git.php.net:php-src: (240 commits)
Do not execute anything after quit or clean command
Fix last commit, and do not output unnecessary information
Stabilize execution, always run destructors and extended file breakpoints
Fix nullptr dereference in clean without exec context
remove dodgy param parser, bring userland breakpoint api inline with PHP7
disable output buffering by default
Add question to reset execution in run/exec/clean
- Updated to version 2014.9 (2014i)
actually remove this
disable output buffering, better breakpoint api for userland, remove hand parsing of params
Fix phpdbg output when outputting via php with active output handlers
Fixed Closure::call() NEWS/UPGRADING
Set engine back to initial state after fatal-ed ev command
Fix eventual stack overflow after clean cmd
Fix listing of files with no absolute path
updated libmagic.patch in master
updated libmagic.patch in 5.6
updated libmagic.patch in 5.5
NEWS
NEWS
...
Diffstat (limited to 'sapi/phpdbg/phpdbg.c')
-rw-r--r-- | sapi/phpdbg/phpdbg.c | 908 |
1 files changed, 540 insertions, 368 deletions
diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index 4388b4f86d..299216fcff 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -1,6 +1,6 @@ /* +----------------------------------------------------------------------+ - | PHP Version 7 | + | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2014 The PHP Group | +----------------------------------------------------------------------+ @@ -28,7 +28,9 @@ #include "phpdbg_list.h" #include "phpdbg_utils.h" #include "phpdbg_set.h" +#include "phpdbg_io.h" #include "zend_alloc.h" +#include "phpdbg_eol.h" /* {{{ remote console headers */ #ifndef _WIN32 @@ -36,12 +38,28 @@ # include <sys/select.h> # include <sys/time.h> # include <sys/types.h> +# include <sys/poll.h> # include <netinet/in.h> # include <unistd.h> # include <arpa/inet.h> #endif /* }}} */ ZEND_DECLARE_MODULE_GLOBALS(phpdbg); +int phpdbg_startup_run = 0; + +static PHP_INI_MH(OnUpdateEol) +{ + if (!new_value) { + return FAILURE; + } + + return phpdbg_eol_global_update(new_value->val TSRMLS_CC); +} + +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("phpdbg.path", "", PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString, socket_path, zend_phpdbg_globals, phpdbg_globals) + STD_PHP_INI_ENTRY("phpdbg.eol", "2", PHP_INI_ALL, OnUpdateEol, socket_path, zend_phpdbg_globals, phpdbg_globals) +PHP_INI_END() static zend_bool phpdbg_booted = 0; @@ -63,20 +81,40 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */ pg->exec = NULL; pg->exec_len = 0; pg->buffer = NULL; + pg->last_was_newline = 1; pg->ops = NULL; pg->vmret = 0; + pg->in_execution = 0; pg->bp_count = 0; 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->io, 0, sizeof(pg->io)); pg->frame.num = 0; + pg->sapi_name_ptr = NULL; + pg->socket_fd = -1; + pg->socket_server_fd = -1; + + pg->req_id = 0; + pg->err_buf.active = 0; + pg->err_buf.type = 0; + + pg->input_buflen = 0; + pg->sigsafe_mem.mem = NULL; + pg->sigsegv_bailout = NULL; + +#ifdef PHP_WIN32 + pg->sigio_watcher_thread = INVALID_HANDLE_VALUE; + memset(&pg->swd, 0, sizeof(struct win32_sigio_watcher_data)); +#endif + + pg->eol = PHPDBG_EOL_LF; } /* }}} */ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ { ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL); + REGISTER_INI_ENTRIES(); + #if PHP_VERSION_ID >= 50500 zend_execute_old = zend_execute_ex; zend_execute_ex = phpdbg_execute_ex; @@ -86,7 +124,7 @@ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ #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); @@ -99,56 +137,56 @@ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ return SUCCESS; } /* }}} */ -static void php_phpdbg_destroy_bp_file(void *brake) /* {{{ */ +static void php_phpdbg_destroy_bp_file(zval *brake) /* {{{ */ { - zend_hash_destroy((HashTable*)brake); + zend_hash_destroy(Z_ARRVAL_P(brake)); } /* }}} */ -static void php_phpdbg_destroy_bp_symbol(void *brake) /* {{{ */ +static void php_phpdbg_destroy_bp_symbol(zval *brake) /* {{{ */ { - efree((char*)((phpdbg_breaksymbol_t*)brake)->symbol); + efree((char *) ((phpdbg_breaksymbol_t *) Z_PTR_P(brake))->symbol); } /* }}} */ -static void php_phpdbg_destroy_bp_opcode(void *brake) /* {{{ */ +static void php_phpdbg_destroy_bp_opcode(zval *brake) /* {{{ */ { - efree((char*)((phpdbg_breakop_t*)brake)->name); + efree((char *) ((phpdbg_breakop_t *) Z_PTR_P(brake))->name); } /* }}} */ -static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */ +static void php_phpdbg_destroy_bp_methods(zval *brake) /* {{{ */ { - zend_hash_destroy((HashTable*)brake); + zend_hash_destroy(Z_ARRVAL_P(brake)); } /* }}} */ -static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */ +static void php_phpdbg_destroy_bp_condition(zval *data) /* {{{ */ { - phpdbg_breakcond_t *brake = (phpdbg_breakcond_t*) data; + phpdbg_breakcond_t *brake = (phpdbg_breakcond_t *) Z_PTR_P(data); if (brake) { if (brake->ops) { TSRMLS_FETCH(); - destroy_op_array( - brake->ops TSRMLS_CC); + destroy_op_array(brake->ops TSRMLS_CC); efree(brake->ops); } - efree((char*)brake->code); + efree((char*) brake->code); } } /* }}} */ -static void php_phpdbg_destroy_registered(void *data) /* {{{ */ +static void php_phpdbg_destroy_registered(zval *data) /* {{{ */ { - zend_function *function = (zend_function*) data; + zend_function *function = (zend_function *) Z_PTR_P(data); + TSRMLS_FETCH(); - destroy_zend_function( - function TSRMLS_CC); + 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_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], 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); @@ -178,6 +216,7 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ 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(file_sources)); zend_hash_destroy(&PHPDBG_G(registered)); zend_hash_destroy(&PHPDBG_G(watchpoints)); zend_llist_destroy(&PHPDBG_G(watchlist_mem)); @@ -186,7 +225,7 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ efree(PHPDBG_G(buffer)); PHPDBG_G(buffer) = NULL; } - + if (PHPDBG_G(exec)) { efree(PHPDBG_G(exec)); PHPDBG_G(exec) = NULL; @@ -224,10 +263,9 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ 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; - int exec_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) { + zend_string *exec; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &exec) == FAILURE) { return; } @@ -235,58 +273,81 @@ static PHP_FUNCTION(phpdbg_exec) struct stat sb; zend_bool result = 1; - if (VCWD_STAT(exec, &sb) != FAILURE) { + if (VCWD_STAT(exec->val, &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); + ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len)); efree(PHPDBG_G(exec)); result = 0; } - PHPDBG_G(exec) = estrndup(exec, exec_len); - PHPDBG_G(exec_len) = exec_len; + PHPDBG_G(exec) = estrndup(exec->val, exec->len); + PHPDBG_G(exec_len) = exec->len; - if (result) + 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); + 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); + 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]) +/* {{{ proto void phpdbg_break() instructs phpdbg to insert a breakpoint at the next opcode */ -static PHP_FUNCTION(phpdbg_break) +static PHP_FUNCTION(phpdbg_break_next) { - if (ZEND_NUM_ARGS() > 0) { - long type = 0; - char *expr = NULL; - int expr_len = 0; - phpdbg_param_t param; + if (zend_parse_parameters_none() == FAILURE && EG(current_execute_data)) { + return; + } + + phpdbg_set_breakpoint_opline_ex((phpdbg_opline_ptr_t) EG(current_execute_data)->opline + 1 TSRMLS_CC); +} /* }}} */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &type, &expr, &expr_len) == FAILURE) { - return; - } +/* {{{ proto void phpdbg_break_file(string file, integer line) */ +static PHP_FUNCTION(phpdbg_break_file) +{ + char *file = NULL; + size_t flen = 0; + long line; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &file, &flen, &line) == FAILURE) { + return; + } - phpdbg_parse_param(expr, expr_len, ¶m TSRMLS_CC); - phpdbg_do_break(¶m TSRMLS_CC); - phpdbg_clear_param(¶m TSRMLS_CC); + phpdbg_set_breakpoint_file(file, line TSRMLS_CC); +} /* }}} */ + +/* {{{ proto void phpdbg_break_method(string class, string method) */ +static PHP_FUNCTION(phpdbg_break_method) +{ + char *class = NULL, *method = NULL; + size_t clen = 0, mlen = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &class, &clen, &method, &mlen) == FAILURE) { + return; + } - } 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_method(class, method TSRMLS_CC); +} /* }}} */ - phpdbg_set_breakpoint_opline_ex( - &EG(active_op_array)->opcodes[opline_num+1] TSRMLS_CC); +/* {{{ proto void phpdbg_break_function(string function) */ +static PHP_FUNCTION(phpdbg_break_function) +{ + char *function = NULL; + size_t function_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &function, &function_len) == FAILURE) { + return; } + + phpdbg_set_breakpoint_symbol(function, function_len TSRMLS_CC); } /* }}} */ /* {{{ proto void phpdbg_clear(void) @@ -294,6 +355,7 @@ static PHP_FUNCTION(phpdbg_break) static PHP_FUNCTION(phpdbg_clear) { zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]); 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]); @@ -338,9 +400,21 @@ static PHP_FUNCTION(phpdbg_prompt) 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_BEGIN_ARG_INFO_EX(phpdbg_break_next_arginfo, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_file_arginfo, 0, 0, 2) + ZEND_ARG_INFO(0, file) + ZEND_ARG_INFO(0, line) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_method_arginfo, 0, 0, 2) + ZEND_ARG_INFO(0, class) + ZEND_ARG_INFO(0, method) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_function_arginfo, 0, 0, 1) + ZEND_ARG_INFO(0, function) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(phpdbg_color_arginfo, 0, 0, 0) @@ -361,7 +435,10 @@ 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_break_next, phpdbg_break_next_arginfo) + PHP_FE(phpdbg_break_file, phpdbg_break_file_arginfo) + PHP_FE(phpdbg_break_method, phpdbg_break_method_arginfo) + PHP_FE(phpdbg_break_function, phpdbg_break_function_arginfo) PHP_FE(phpdbg_exec, phpdbg_exec_arginfo) PHP_FE(phpdbg_color, phpdbg_color_arginfo) PHP_FE(phpdbg_prompt, phpdbg_prompt_arginfo) @@ -426,7 +503,12 @@ 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); + if (PHPDBG_G(flags) & PHPDBG_IN_EVAL) { + phpdbg_error("eval", "msg=\"%s\"", "%s", message); + return; + } + + phpdbg_error("php", "msg=\"%s\"", "%s", message); switch (PG(last_error_type)) { case E_ERROR: @@ -434,47 +516,60 @@ static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */ 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 - ); - } + case E_RECOVERABLE_ERROR: { + const char *file_char = zend_get_executed_filename(TSRMLS_C); + zend_string *file = zend_string_init(file_char, strlen(file_char), 0); + phpdbg_list_file(file, 3, zend_get_executed_lineno(TSRMLS_C) - 1, zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC); + efree(file); do { - switch (phpdbg_interactive(TSRMLS_C)) { + switch (phpdbg_interactive(1 TSRMLS_CC)) { case PHPDBG_LEAVE: case PHPDBG_FINISH: case PHPDBG_UNTIL: case PHPDBG_NEXT: return; } - } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); - + } while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING)); + } } - } else fprintf(stdout, "%s\n", message); + } else { + fprintf(stdout, "%s\n", message); + } } /* }}} */ static int php_sapi_phpdbg_deactivate(TSRMLS_D) /* {{{ */ { + if ((PHPDBG_G(flags) & PHPDBG_IS_STOPPING) == PHPDBG_IS_CLEANING) { + zend_phpdbg_globals *pg = PHPDBG_G(backup) = calloc(1, sizeof(zend_phpdbg_globals)); + + php_phpdbg_globals_ctor(pg); + + pg->exec = strndup(PHPDBG_G(exec), PHPDBG_G(exec_len)); + pg->exec_len = PHPDBG_G(exec_len); + pg->oplog = PHPDBG_G(oplog); + pg->prompt[0] = PHPDBG_G(prompt)[0]; + pg->prompt[1] = PHPDBG_G(prompt)[1]; + memcpy(pg->colors, PHPDBG_G(colors), sizeof(pg->colors)); + pg->eol = PHPDBG_G(eol); + pg->flags = PHPDBG_G(flags) & PHPDBG_PRESERVE_FLAGS_MASK; + } + 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 = ""; + size_t len; + char *docroot = ""; /* In phpdbg mode, we consider the environment to be a part of the server variables */ @@ -482,43 +577,67 @@ static void php_sapi_phpdbg_register_vars(zval *track_vars_array TSRMLS_DC) /* { 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, "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_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, "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); + 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)) { + /* any old docroot will do */ + len = 0; + 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) /* {{{ */ +static inline size_t php_sapi_phpdbg_ub_write(const char *message, size_t length TSRMLS_DC) /* {{{ */ { - return phpdbg_write("%s", message); + if (PHPDBG_G(socket_fd) != -1 && !(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) { + send(PHPDBG_G(socket_fd), message, length, 0); + } + return phpdbg_script(P_STDOUT, "%.*s", length, message); } /* }}} */ +/* beginning of struct, see main/streams/plain_wrapper.c line 111 */ +typedef struct { + FILE *file; + int fd; +} php_stdio_stream_data; + +static size_t phpdbg_stdiop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) { + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + + while (data->fd >= 0) { + struct stat stat[3]; + memset(stat, 0, sizeof(stat)); + if (((fstat(fileno(stderr), &stat[2]) < 0) & (fstat(fileno(stdout), &stat[0]) < 0)) | (fstat(data->fd, &stat[1]) < 0)) { + break; + } + + if (stat[0].st_dev == stat[1].st_dev && stat[0].st_ino == stat[1].st_ino) { + phpdbg_script(P_STDOUT, "%.*s", (int) count, buf); + return count; + } + if (stat[2].st_dev == stat[1].st_dev && stat[2].st_ino == stat[1].st_ino) { + phpdbg_script(P_STDERR, "%.*s", (int) count, buf); + return count; + } + break; + } + + return PHPDBG_G(php_stdiop_write)(stream, buf, count TSRMLS_CC); +} + #if PHP_VERSION_ID >= 50700 static inline void php_sapi_phpdbg_flush(void *context TSRMLS_DC) /* {{{ */ { @@ -528,29 +647,24 @@ static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */ TSRMLS_FETCH(); #endif - fflush(PHPDBG_G(io)[PHPDBG_STDOUT]); + if (!phpdbg_active_sigsafe_mem(TSRMLS_C)) { + fflush(PHPDBG_G(io)[PHPDBG_STDOUT].ptr); + } } /* }}} */ /* copied from sapi/cli/php_cli.c cli_register_file_handles */ static void phpdbg_register_file_handles(TSRMLS_D) /* {{{ */ { - zval *zin, *zout, *zerr; + zval zin, zout, zerr; php_stream *s_in, *s_out, *s_err; php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL; zend_constant ic, oc, ec; - MAKE_STD_ZVAL(zin); - MAKE_STD_ZVAL(zout); - MAKE_STD_ZVAL(zerr); - s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in); s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out); s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err); if (s_in==NULL || s_out==NULL || s_err==NULL) { - FREE_ZVAL(zin); - FREE_ZVAL(zout); - FREE_ZVAL(zerr); if (s_in) php_stream_close(s_in); if (s_out) php_stream_close(s_out); if (s_err) php_stream_close(s_err); @@ -563,34 +677,27 @@ static void phpdbg_register_file_handles(TSRMLS_D) /* {{{ */ s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE; #endif - php_stream_to_zval(s_in, zin); - php_stream_to_zval(s_out, zout); - php_stream_to_zval(s_err, zerr); + php_stream_to_zval(s_in, &zin); + php_stream_to_zval(s_out, &zout); + php_stream_to_zval(s_err, &zerr); - ic.value = *zin; + ic.value = zin; ic.flags = CONST_CS; - ic.name = zend_strndup(ZEND_STRL("STDIN")); - ic.name_len = sizeof("STDIN"); + ic.name = zend_string_init(ZEND_STRL("STDIN"), 0); ic.module_number = 0; zend_register_constant(&ic TSRMLS_CC); - oc.value = *zout; + oc.value = zout; oc.flags = CONST_CS; - oc.name = zend_strndup(ZEND_STRL("STDOUT")); - oc.name_len = sizeof("STDOUT"); + oc.name = zend_string_init(ZEND_STRL("STDOUT"), 0); oc.module_number = 0; zend_register_constant(&oc TSRMLS_CC); - ec.value = *zerr; + ec.value = zerr; ec.flags = CONST_CS; - ec.name = zend_strndup(ZEND_STRL("STDERR")); - ec.name_len = sizeof("STDERR"); + ec.name = zend_string_init(ZEND_STRL("STDERR"), 0); ec.module_number = 0; zend_register_constant(&ec TSRMLS_CC); - - FREE_ZVAL(zin); - FREE_ZVAL(zout); - FREE_ZVAL(zerr); } /* }}} */ @@ -648,6 +755,7 @@ const opt_struct OPTIONS[] = { /* {{{ */ {'l', 1, "listen"}, {'a', 1, "address-or-any"}, #endif + {'x', 0, "xml output"}, {'V', 0, "version"}, {'-', 0, NULL} }; /* }}} */ @@ -660,14 +768,14 @@ const char phpdbg_ini_hardcoded[] = "log_errors=On\n" "max_execution_time=0\n" "max_input_time=-1\n" -"error_log=\n\0"; +"error_log=\n" +"output_buffering=off\n\0"; /* overwriteable ini defaults must be set in phpdbg_ini_defaults() */ #define INI_DEFAULT(name, value) \ + ZVAL_STRINGL(&tmp, value, sizeof(value) - 1); \ 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); + zend_hash_str_update(configuration_hash, name, sizeof(name) - 1, &tmp); void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */ { @@ -679,17 +787,25 @@ 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_xml("<intros>"); + phpdbg_notice("intro", "version=\"%s\"", "Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION); + phpdbg_writeln("intro", "help=\"help\"", "To get help using phpdbg type \"help\" and press enter"); + phpdbg_notice("intro", "report=\"%s\"", "Please report bugs to <%s>", PHPDBG_ISSUES); + phpdbg_xml("</intros>"); + } else if (phpdbg_startup_run == 0) { + if (!(PHPDBG_G(flags) & PHPDBG_WRITE_XML)) { + phpdbg_notice(NULL, NULL, "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))); + phpdbg_write("cleaninfo", "classes=\"%d\" functions=\"%d\" constants=\"%d\" includes=\"%d\"", + "Classes %d\n" + "Functions %d\n" + "Constants %d\n" + "Includes %d\n", + zend_hash_num_elements(EG(class_table)), + zend_hash_num_elements(EG(function_table)), + zend_hash_num_elements(EG(zend_constants)), + zend_hash_num_elements(&EG(included_files))); } } /* }}} */ @@ -697,163 +813,129 @@ 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 { + if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) { /* we quit remote consoles on recv SIGINT */ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { - PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; + PHPDBG_G(flags) |= PHPDBG_IS_STOPPING; 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); - } - } - } + } else { + /* set signalled only when not interactive */ + if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) { + if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) { + char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1]; + + phpdbg_set_sigsafe_mem(mem TSRMLS_CC); + zend_try { + phpdbg_force_interruption(TSRMLS_C); + } zend_end_try() + phpdbg_clear_sigsafe_mem(TSRMLS_C); + return; } + PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED; } } - - 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]); +static void phpdbg_remote_close(int socket, FILE *stream) { + if (socket >= 0) { + phpdbg_close_socket(socket); } - if (streams[0]) { - fclose(streams[0]); + if (stream) { + fclose(stream); } +} - if ((*socket)[1] >= 0) { - shutdown( - (*socket)[1], SHUT_RDWR); - close((*socket)[1]); - } +/* don't inline this, want to debug it easily, will inline when done */ +static int phpdbg_remote_init(const char* address, unsigned short port, int server, int *socket, FILE **stream TSRMLS_DC) { + phpdbg_remote_close(*socket, *stream); - if (streams[1]) { - fclose(streams[1]); + if (server < 0) { + phpdbg_rlog(fileno(stderr), "Initializing connection on %s:%u failed", address, port); + + return FAILURE; } -} /* }}} */ -/* don't inline this, want to debug it easily, will inline when done */ + phpdbg_rlog(fileno(stderr), "accepting connections on %s:%u", address, port); + { + struct sockaddr_storage address; + socklen_t size = sizeof(address); + char buffer[20] = {0}; + /* XXX error checks */ + memset(&address, 0, size); + *socket = accept(server, (struct sockaddr *) &address, &size); + inet_ntop(AF_INET, &(((struct sockaddr_in *)&address)->sin_addr), buffer, sizeof(buffer)); -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]); + phpdbg_rlog(fileno(stderr), "connection established from %s", buffer); } - 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]); - } +#ifndef _WIN32 + dup2(*socket, fileno(stdout)); + dup2(*socket, fileno(stdin)); - if ((*listen)[1] < 0) { - phpdbg_rlog(stderr, - "console failed to initialize (stdout) on %s:%d", address, port[1]); - } + setbuf(stdout, NULL); - if ((*listen)[0] >= 0) { - close((*listen)[0]); - } + *stream = fdopen(*socket, "r+"); - if ((*listen)[1] >= 0) { - close((*listen)[1]); - } + phpdbg_set_async_io(*socket); +#endif + return SUCCESS; +} - return FAILURE; - } +#ifndef _WIN32 +/* This function *strictly* assumes that SIGIO is *only* used on the remote connection stream */ +void phpdbg_sigio_handler(int sig, siginfo_t *info, void *context) /* {{{ */ +{ + int flags; + size_t newlen; + size_t i/*, last_nl*/; + TSRMLS_FETCH(); - phpdbg_close_sockets(socket, streams); +// if (!(info->si_band & POLLIN)) { +// return; /* Not interested in writeablility etc., just interested in incoming data */ +// } - 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}; + /* only non-blocking reading, avoid non-blocking writing */ + flags = fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_GETFL, 0); + fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags | O_NONBLOCK); - { - memset(&address, 0, size); - (*socket)[0] = accept( - (*listen)[0], (struct sockaddr *) &address, &size); - inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer)); + do { + char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1]; + size_t off = 0; - phpdbg_rlog(stderr, "connection (stdin) from %s", buffer); + if ((newlen = recv(PHPDBG_G(io)[PHPDBG_STDIN].fd, mem, PHPDBG_SIGSAFE_MEM_SIZE, MSG_PEEK)) == (size_t) -1) { + break; } - - { - 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); + for (i = 0; i < newlen; i++) { + switch (mem[off + i]) { + case '\x03': /* ^C char */ + if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) { + break; /* or quit ??? */ + } + if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) { + phpdbg_set_sigsafe_mem(mem TSRMLS_CC); + zend_try { + phpdbg_force_interruption(TSRMLS_C); + } zend_end_try(); + phpdbg_clear_sigsafe_mem(TSRMLS_C); + break; + } + if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) { + PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED; + } + break; +/* case '\n': + zend_llist_add_element(PHPDBG_G(stdin), strndup() + last_nl = PHPDBG_G(stdin_buf).len + i; + break; +*/ } } - } - - dup2((*socket)[0], fileno(stdin)); - dup2((*socket)[1], fileno(stdout)); - - setbuf(stdout, NULL); + off += i; + } while (0); - streams[0] = fdopen((*socket)[0], "r"); - streams[1] = fdopen((*socket)[1], "w"); - return SUCCESS; + fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags); } /* }}} */ void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */ @@ -864,6 +946,9 @@ void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */ switch (sig) { case SIGBUS: case SIGSEGV: + if (PHPDBG_G(sigsegv_bailout)) { + LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE); + } is_handled = phpdbg_watchpoint_segfault_handler(info, context TSRMLS_CC); if (is_handled == FAILURE) { #ifdef ZEND_SIGNALS @@ -915,8 +1000,7 @@ int main(int argc, char **argv) /* {{{ */ zend_ulong zend_extensions_len = 0L; zend_bool ini_ignore; char *ini_override; - char *exec; - size_t exec_len; + char *exec = NULL; char *init_file; size_t init_file_len; zend_bool init_file_default; @@ -925,10 +1009,10 @@ int main(int argc, char **argv) /* {{{ */ zend_ulong flags; char *php_optarg; int php_optind, opt, show_banner = 1; - long cleaning = 0; + long cleaning = -1; zend_bool remote = 0; - int run = 0; int step = 0; + zend_phpdbg_globals *settings = NULL; #ifdef _WIN32 char *bp_tmp_file = NULL; @@ -938,10 +1022,10 @@ int main(int argc, char **argv) /* {{{ */ #ifndef _WIN32 char *address; - int listen[2]; - int server[2]; - int socket[2]; - FILE* streams[2] = {NULL, NULL}; + int listen = -1; + int server = -1; + int socket = -1; + FILE* stream = NULL; #endif #ifdef ZTS @@ -949,19 +1033,15 @@ int main(int argc, char **argv) /* {{{ */ #endif #ifndef _WIN32 + struct sigaction sigio_struct; struct sigaction signal_struct; signal_struct.sa_sigaction = phpdbg_signal_handler; signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER; + sigio_struct.sa_sigaction = phpdbg_sigio_handler; + sigio_struct.sa_flags = SA_SIGINFO; + 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 @@ -991,7 +1071,7 @@ phpdbg_main: } if (!bp_tmp_file) { - phpdbg_error("Unable to create temporary file"); + phpdbg_error("tmpfile", "", "Unable to create temporary file"); return 1; } #else @@ -1007,8 +1087,6 @@ phpdbg_main: 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; @@ -1018,14 +1096,16 @@ phpdbg_main: php_optarg = NULL; php_optind = 1; opt = 0; - run = 0; step = 0; sapi_name = NULL; + if (settings) { + exec = settings->exec; + } while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { switch (opt) { case 'r': - run++; + phpdbg_startup_run++; break; case 'n': ini_ignore = 1; @@ -1126,20 +1206,12 @@ phpdbg_main: 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); - } + /* if you pass a listen port, we will read and write on listen port */ + case 'l': /* set listen ports */ + if (sscanf(php_optarg, "%d", &listen) != 1) { + listen = 8000; } - } break; + break; case 'a': { /* set bind address */ free(address); @@ -1149,6 +1221,10 @@ phpdbg_main: } break; #endif + case 'x': + flags |= PHPDBG_WRITE_XML; + break; + case 'V': { sapi_startup(phpdbg); phpdbg->startup(phpdbg); @@ -1168,10 +1244,8 @@ phpdbg_main: } /* set exec if present on command line */ - if ((argc > php_optind) && (strcmp(argv[php_optind-1],"--") != SUCCESS)) - { - exec_len = strlen(argv[php_optind]); - if (exec_len) { + if (!exec && (argc > php_optind) && (strcmp(argv[php_optind-1], "--") != SUCCESS)) { + if (strlen(argv[php_optind])) { if (exec) { free(exec); } @@ -1180,19 +1254,6 @@ phpdbg_main: php_optind++; } -#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; } @@ -1247,19 +1308,58 @@ phpdbg_main: EXCEPTION_POINTERS *xp; __try { #endif - zend_mm_heap *mm_heap = phpdbg_mm_get_heap(); + zend_mm_heap *mm_heap; + void* (*_malloc)(size_t); + void (*_free)(void*); + void* (*_realloc)(void*, size_t); + + /* set flags from command line */ + PHPDBG_G(flags) = flags; - if (mm_heap->use_zend_alloc) { - mm_heap->_malloc = phpdbg_malloc_wrapper; - mm_heap->_realloc = phpdbg_realloc_wrapper; - mm_heap->_free = phpdbg_free_wrapper; - mm_heap->use_zend_alloc = 0; + if (settings) { +#ifdef ZTS + *((zend_phpdbg_globals *) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(phpdbg_globals_id)]) = *settings; +#else + phpdbg_globals = *settings; +#endif + } + + /* setup remote server if necessary */ + if (!cleaning && listen > 0) { + server = phpdbg_open_socket(address, listen TSRMLS_CC); + if (-1 > server || phpdbg_remote_init(address, listen, server, &socket, &stream TSRMLS_CC) == FAILURE) { + exit(0); + } + +#ifndef _WIN32 + sigaction(SIGIO, &sigio_struct, NULL); +#endif + + /* set remote flag to stop service shutting down upon quit */ + remote = 1; + } + + mm_heap = phpdbg_mm_get_heap(); + zend_mm_get_custom_handlers(mm_heap, &_malloc, &_free, &_realloc); + + if (!_malloc) { + _malloc = phpdbg_malloc_wrapper; + } + if (!_realloc) { + _realloc = phpdbg_realloc_wrapper; + } + if (!_free) { + _free = phpdbg_free_wrapper; } zend_activate(TSRMLS_C); - PHPDBG_G(original_free_function) = mm_heap->_free; - mm_heap->_free = phpdbg_watch_efree; + phpdbg_init_list(TSRMLS_C); + + PHPDBG_G(original_free_function) = _free; + _free = phpdbg_watch_efree; + + zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc); phpdbg_setup_watchpoints(TSRMLS_C); @@ -1277,27 +1377,30 @@ phpdbg_main: sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); #endif + PHPDBG_G(sapi_name_ptr) = sapi_name; + + php_output_activate(TSRMLS_C); + php_output_deactivate(TSRMLS_C); + + php_output_activate(TSRMLS_C); + if (php_request_startup(TSRMLS_C) == SUCCESS) { int i; - - SG(request_info).argc = argc - php_optind + 1; + + SG(request_info).argc = argc - php_optind + 1; SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *)); for (i = SG(request_info).argc; --i;) { SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]); } - SG(request_info).argv[i] = exec ? estrndup(exec, exec_len) : estrdup(""); + SG(request_info).argv[i] = exec ? estrdup(exec) : estrdup(""); php_hash_environment(TSRMLS_C); } - /* make sure to turn off buffer for ev command */ - php_output_activate(TSRMLS_C); - php_output_deactivate(TSRMLS_C); - /* do not install sigint handlers for remote consoles */ /* sending SIGINT then provides a decent way of shutting down the server */ #ifndef _WIN32 - if (listen[0] < 0) { + if (listen < 0) { #endif #if defined(ZEND_SIGNALS) && !defined(_WIN32) zend_try { zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); } zend_end_try(); @@ -1310,34 +1413,58 @@ phpdbg_main: PG(modules_activated) = 0; - /* set flags from command line */ - PHPDBG_G(flags) = flags; - #ifndef _WIN32 /* setup io here */ - if (streams[0] && streams[1]) { + if (remote) { 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; +#ifndef _WIN32 + PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin; + PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin); + PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout; + PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout); +#else + /* XXX this is a complete mess here with FILE/fd/SOCKET, + we should let only one to survive probably. Need + a clean separation whether it's a remote or local + prompt. And what is supposed to go as user interaction, + error log, etc. */ + if (remote) { + PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin; + PHPDBG_G(io)[PHPDBG_STDIN].fd = socket; + PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout; + PHPDBG_G(io)[PHPDBG_STDOUT].fd = socket; + } else { + PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin; + PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin); + PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout; + PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout); + } +#endif + PHPDBG_G(io)[PHPDBG_STDERR].ptr = stderr; + PHPDBG_G(io)[PHPDBG_STDERR].fd = fileno(stderr); + +#ifndef _WIN32 + PHPDBG_G(php_stdiop_write) = php_stream_stdio_ops.write; + php_stream_stdio_ops.write = phpdbg_stdiop_write; +#endif if (exec) { /* set execution context */ PHPDBG_G(exec) = phpdbg_resolve_path(exec TSRMLS_CC); PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec)); free(exec); + exec = NULL; } 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); + phpdbg_error("oplog", "path=\"%s\"", "Failed to open oplog %s", oplog_file); } free(oplog_file); } @@ -1348,26 +1475,25 @@ phpdbg_main: phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green") TSRMLS_CC); /* set default prompt */ - phpdbg_set_prompt(PROMPT TSRMLS_CC); + phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT TSRMLS_CC); /* Make stdin, stdout and stderr accessible from PHP scripts */ phpdbg_register_file_handles(TSRMLS_C); - if (show_banner) { + if (show_banner && cleaning < 2) { /* print blurb */ - phpdbg_welcome((cleaning > 0) TSRMLS_CC); + phpdbg_welcome(cleaning == 1 TSRMLS_CC); } - /* auto compile */ - if (PHPDBG_G(exec)) { - phpdbg_compile(TSRMLS_C); - } + cleaning = -1; /* initialize from file */ PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING; zend_try { phpdbg_init(init_file, init_file_len, init_file_default TSRMLS_CC); + PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT; phpdbg_try_file_init(bp_tmp_file, strlen(bp_tmp_file), 0 TSRMLS_CC); + PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT; } zend_end_try(); PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING; @@ -1376,18 +1502,29 @@ phpdbg_main: goto phpdbg_out; } + /* auto compile */ + if (PHPDBG_G(exec)) { + if (settings) { + PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT; + } + phpdbg_compile(TSRMLS_C); + PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT; + } + /* 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 TSRMLS_CC); - if (run > 1) { + if (phpdbg_startup_run) { + zend_try { + PHPDBG_COMMAND_HANDLER(run)(NULL TSRMLS_CC); + } zend_end_try(); + if (phpdbg_startup_run > 1) { /* if -r is on the command line more than once just quit */ goto phpdbg_out; } + phpdbg_startup_run = 0; } /* #ifndef for making compiler shutting up */ @@ -1397,11 +1534,13 @@ phpdbg_interact: /* phpdbg main() */ do { zend_try { - phpdbg_interactive(TSRMLS_C); + phpdbg_interactive(1 TSRMLS_CC); } zend_catch { if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) { FILE *bp_tmp_fp = fopen(bp_tmp_file, "w"); + PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT; phpdbg_export_breakpoints(bp_tmp_fp TSRMLS_CC); + PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT; fclose(bp_tmp_fp); cleaning = 1; } else { @@ -1415,11 +1554,10 @@ phpdbg_interact: if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { /* renegociate connections */ - phpdbg_open_sockets( - address, listen, &server, &socket, streams); + phpdbg_remote_init(address, listen, server, &socket, &stream TSRMLS_CC); /* set streams */ - if (streams[0] && streams[1]) { + if (stream) { PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING; } @@ -1433,8 +1571,13 @@ phpdbg_interact: } #endif } zend_end_try(); - } while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); - + } while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING)); + + + if (PHPDBG_G(exec) && (PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) { + exec = strdup(PHPDBG_G(exec)); /* preserve exec, don't reparse that from cmd */ + } + /* this must be forced */ CG(unclean_shutdown) = 0; @@ -1451,11 +1594,16 @@ phpdbg_out: #ifdef _WIN32 } __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) { - phpdbg_error("Access violation (Segementation fault) encountered\ntrying to abort cleanly..."); + phpdbg_error("segfault", "", "Access violation (Segementation fault) encountered\ntrying to abort cleanly..."); } phpdbg_out: #endif - + + if (cleaning <= 0) { + PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING; + cleaning = -1; + } + { int i; /* free argv */ @@ -1465,11 +1613,15 @@ phpdbg_out: efree(SG(request_info).argv); } +#ifndef _WIN32 + /* reset it... else we risk a stack overflow upon next run (when clean'ing) */ + php_stream_stdio_ops.write = PHPDBG_G(php_stdiop_write); +#endif + #ifndef ZTS /* force cleanup of auto and core globals */ zend_hash_clean(CG(auto_globals)); - memset( - &core_globals, 0, sizeof(php_core_globals)); + memset( &core_globals, 0, sizeof(php_core_globals)); #endif if (ini_entries) { free(ini_entries); @@ -1478,14 +1630,34 @@ phpdbg_out: if (ini_override) { free(ini_override); } - + /* this must be forced */ CG(unclean_shutdown) = 0; - + /* this is just helpful */ PG(report_memleaks) = 0; - php_request_shutdown((void*)0); + if ((PHPDBG_G(flags) & (PHPDBG_IS_CLEANING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_CLEANING) { + php_free_shutdown_functions(TSRMLS_C); + zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC); + } + + /* sapi_module.deactivate is where to backup things, last chance before mm_shutdown... */ + + zend_try { + php_request_shutdown(NULL); + } zend_end_try(); + + if ((PHPDBG_G(flags) & (PHPDBG_IS_QUITTING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_RUNNING) { + phpdbg_notice("stop", "type=\"normal\"", "Script ended normally"); + cleaning++; + } + + if ((PHPDBG_G(flags) & PHPDBG_IS_STOPPING) == PHPDBG_IS_CLEANING) { + settings = PHPDBG_G(backup); + } + + php_output_deactivate(TSRMLS_C); zend_try { php_module_shutdown(TSRMLS_C); @@ -1495,10 +1667,10 @@ phpdbg_out: } - if (cleaning || remote) { + if (cleaning > 0 || remote) { goto phpdbg_main; } - + #ifdef ZTS /* bugggy */ /* tsrm_shutdown(); */ @@ -1510,10 +1682,10 @@ phpdbg_out: } #endif - if (sapi_name) { - free(sapi_name); + if (PHPDBG_G(sapi_name_ptr)) { + free(PHPDBG_G(sapi_name_ptr)); } - + #ifdef _WIN32 free(bp_tmp_file); #else |