mirror of
https://github.com/php/php-src.git
synced 2024-11-25 02:44:58 +08:00
1664 lines
44 KiB
C
1664 lines
44 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP Version 5 |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 1997-2015 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> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#if !defined(ZEND_SIGNALS) || defined(_WIN32)
|
|
# 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"
|
|
#include "phpdbg_io.h"
|
|
#include "zend_alloc.h"
|
|
#include "phpdbg_eol.h"
|
|
|
|
#include "ext/standard/basic_functions.h"
|
|
|
|
/* {{{ remote console headers */
|
|
#ifndef _WIN32
|
|
# include <sys/socket.h>
|
|
# 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);
|
|
}
|
|
|
|
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;
|
|
|
|
#if PHP_VERSION_ID >= 50500
|
|
void (*zend_execute_old)(zend_execute_data *execute_data);
|
|
#else
|
|
void (*zend_execute_old)(zend_op_array *op_array);
|
|
#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->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;
|
|
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;
|
|
#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(zval *brake) /* {{{ */
|
|
{
|
|
zend_hash_destroy(Z_ARRVAL_P(brake));
|
|
} /* }}} */
|
|
|
|
static void php_phpdbg_destroy_bp_symbol(zval *brake) /* {{{ */
|
|
{
|
|
efree((char *) ((phpdbg_breaksymbol_t *) Z_PTR_P(brake))->symbol);
|
|
} /* }}} */
|
|
|
|
static void php_phpdbg_destroy_bp_opcode(zval *brake) /* {{{ */
|
|
{
|
|
efree((char *) ((phpdbg_breakop_t *) Z_PTR_P(brake))->name);
|
|
} /* }}} */
|
|
|
|
|
|
static void php_phpdbg_destroy_bp_methods(zval *brake) /* {{{ */
|
|
{
|
|
zend_hash_destroy(Z_ARRVAL_P(brake));
|
|
} /* }}} */
|
|
|
|
static void php_phpdbg_destroy_bp_condition(zval *data) /* {{{ */
|
|
{
|
|
phpdbg_breakcond_t *brake = (phpdbg_breakcond_t *) Z_PTR_P(data);
|
|
|
|
if (brake) {
|
|
if (brake->ops) {
|
|
|
|
destroy_op_array(brake->ops);
|
|
efree(brake->ops);
|
|
}
|
|
efree((char*) brake->code);
|
|
}
|
|
} /* }}} */
|
|
|
|
static void php_phpdbg_destroy_registered(zval *data) /* {{{ */
|
|
{
|
|
zend_function *function = (zend_function *) Z_PTR_P(data);
|
|
|
|
|
|
destroy_zend_function(function);
|
|
} /* }}} */
|
|
|
|
|
|
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_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);
|
|
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(file_sources));
|
|
zend_hash_destroy(&PHPDBG_G(registered));
|
|
zend_hash_destroy(&PHPDBG_G(watchpoints));
|
|
zend_llist_destroy(&PHPDBG_G(watchlist_mem));
|
|
|
|
if (PHPDBG_G(buffer)) {
|
|
efree(PHPDBG_G(buffer));
|
|
PHPDBG_G(buffer) = NULL;
|
|
}
|
|
|
|
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));
|
|
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)
|
|
{
|
|
zend_string *exec;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &exec) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
{
|
|
struct stat sb;
|
|
zend_bool result = 1;
|
|
|
|
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));
|
|
efree(PHPDBG_G(exec));
|
|
result = 0;
|
|
}
|
|
|
|
PHPDBG_G(exec) = estrndup(exec->val, exec->len);
|
|
PHPDBG_G(exec_len) = exec->len;
|
|
|
|
if (result) {
|
|
ZVAL_TRUE(return_value);
|
|
}
|
|
} else {
|
|
zend_error(E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", exec);
|
|
ZVAL_FALSE(return_value);
|
|
}
|
|
} else {
|
|
zend_error(E_WARNING, "Failed to set execution context (%s) the file does not exist", exec);
|
|
|
|
ZVAL_FALSE(return_value);
|
|
}
|
|
}
|
|
} /* }}} */
|
|
|
|
/* {{{ proto void phpdbg_break()
|
|
instructs phpdbg to insert a breakpoint at the next opcode */
|
|
static PHP_FUNCTION(phpdbg_break_next)
|
|
{
|
|
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);
|
|
} /* }}} */
|
|
|
|
/* {{{ 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(), "sl", &file, &flen, &line) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
phpdbg_set_breakpoint_file(file, line);
|
|
} /* }}} */
|
|
|
|
/* {{{ 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(), "ss", &class, &clen, &method, &mlen) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
phpdbg_set_breakpoint_method(class, method);
|
|
} /* }}} */
|
|
|
|
/* {{{ 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(), "s", &function, &function_len) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
phpdbg_set_breakpoint_symbol(function, function_len);
|
|
} /* }}} */
|
|
|
|
/* {{{ 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_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]);
|
|
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 = 0L;
|
|
char *color = NULL;
|
|
size_t color_len = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "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);
|
|
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 = NULL;
|
|
size_t prompt_len = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &prompt, &prompt_len) == FAILURE) {
|
|
return;
|
|
}
|
|
|
|
phpdbg_set_prompt(prompt);
|
|
} /* }}} */
|
|
|
|
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)
|
|
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_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)
|
|
#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(void) /* {{{ */
|
|
{
|
|
return NULL;
|
|
} /* }}} */
|
|
|
|
static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s) /* {{{ */
|
|
{
|
|
return 0;
|
|
}
|
|
/* }}} */
|
|
|
|
static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */
|
|
{
|
|
/* 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) /* {{{ */
|
|
{
|
|
}
|
|
/* }}} */
|
|
|
|
static void php_sapi_phpdbg_log_message(char *message) /* {{{ */
|
|
{
|
|
/*
|
|
* We must not request TSRM before being boot
|
|
*/
|
|
if (phpdbg_booted) {
|
|
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:
|
|
case E_CORE_ERROR:
|
|
case E_COMPILE_ERROR:
|
|
case E_USER_ERROR:
|
|
case E_PARSE:
|
|
case E_RECOVERABLE_ERROR: {
|
|
const char *file_char = zend_get_executed_filename();
|
|
zend_string *file = zend_string_init(file_char, strlen(file_char), 0);
|
|
phpdbg_list_file(file, 3, zend_get_executed_lineno() - 1, zend_get_executed_lineno());
|
|
efree(file);
|
|
|
|
do {
|
|
switch (phpdbg_interactive(1)) {
|
|
case PHPDBG_LEAVE:
|
|
case PHPDBG_FINISH:
|
|
case PHPDBG_UNTIL:
|
|
case PHPDBG_NEXT:
|
|
return;
|
|
}
|
|
} while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
|
|
}
|
|
}
|
|
} else {
|
|
fprintf(stdout, "%s\n", message);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
static int php_sapi_phpdbg_deactivate(void) /* {{{ */
|
|
{
|
|
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 = zend_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) /* {{{ */
|
|
{
|
|
size_t 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);
|
|
|
|
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)) {
|
|
php_register_variable("PHP_SELF", PHPDBG_G(exec), track_vars_array);
|
|
}
|
|
if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
|
|
php_register_variable("SCRIPT_NAME", PHPDBG_G(exec), track_vars_array);
|
|
}
|
|
|
|
if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
|
|
php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec), track_vars_array);
|
|
}
|
|
if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
|
|
php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec), track_vars_array);
|
|
}
|
|
}
|
|
|
|
/* any old docroot will do */
|
|
len = 0;
|
|
if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", &docroot, len, &len)) {
|
|
php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
static inline size_t php_sapi_phpdbg_ub_write(const char *message, size_t length) /* {{{ */
|
|
{
|
|
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) {
|
|
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);
|
|
}
|
|
|
|
#if PHP_VERSION_ID >= 50700
|
|
static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */
|
|
{
|
|
#else
|
|
static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */
|
|
{
|
|
#endif
|
|
|
|
if (!phpdbg_active_sigsafe_mem()) {
|
|
fflush(PHPDBG_G(io)[PHPDBG_STDOUT].ptr);
|
|
}
|
|
} /* }}} */
|
|
|
|
/* copied from sapi/cli/php_cli.c cli_register_file_handles */
|
|
static void phpdbg_register_file_handles(void) /* {{{ */
|
|
{
|
|
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;
|
|
|
|
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) {
|
|
if (s_in) php_stream_close(s_in);
|
|
if (s_out) php_stream_close(s_out);
|
|
if (s_err) php_stream_close(s_err);
|
|
return;
|
|
}
|
|
|
|
#if PHP_DEBUG
|
|
/* do not close stdout and stderr */
|
|
s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
|
|
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);
|
|
|
|
ic.value = zin;
|
|
ic.flags = CONST_CS;
|
|
ic.name = zend_string_init(ZEND_STRL("STDIN"), 0);
|
|
ic.module_number = 0;
|
|
zend_register_constant(&ic);
|
|
|
|
oc.value = zout;
|
|
oc.flags = CONST_CS;
|
|
oc.name = zend_string_init(ZEND_STRL("STDOUT"), 0);
|
|
oc.module_number = 0;
|
|
zend_register_constant(&oc);
|
|
|
|
ec.value = zerr;
|
|
ec.flags = CONST_CS;
|
|
ec.name = zend_string_init(ZEND_STRL("STDERR"), 0);
|
|
ec.module_number = 0;
|
|
zend_register_constant(&ec);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ 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"},
|
|
{'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
|
|
{'x', 0, "xml output"},
|
|
{'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"
|
|
"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); \
|
|
zend_hash_str_update(configuration_hash, name, sizeof(name) - 1, &tmp);
|
|
|
|
void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */
|
|
{
|
|
zval tmp;
|
|
INI_DEFAULT("report_zend_debug", "0");
|
|
} /* }}} */
|
|
|
|
static void phpdbg_welcome(zend_bool cleaning) /* {{{ */
|
|
{
|
|
/* print blurb */
|
|
if (!cleaning) {
|
|
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_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)));
|
|
}
|
|
} /* }}} */
|
|
|
|
static inline void phpdbg_sigint_handler(int signo) /* {{{ */
|
|
{
|
|
|
|
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_STOPPING;
|
|
zend_bailout();
|
|
}
|
|
} 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);
|
|
zend_try {
|
|
phpdbg_force_interruption();
|
|
} zend_end_try()
|
|
phpdbg_clear_sigsafe_mem();
|
|
return;
|
|
}
|
|
PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
|
|
}
|
|
}
|
|
} /* }}} */
|
|
|
|
static void phpdbg_remote_close(int socket, FILE *stream) {
|
|
if (socket >= 0) {
|
|
phpdbg_close_socket(socket);
|
|
}
|
|
|
|
if (stream) {
|
|
fclose(stream);
|
|
}
|
|
}
|
|
|
|
/* 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) {
|
|
phpdbg_remote_close(*socket, *stream);
|
|
|
|
if (server < 0) {
|
|
phpdbg_rlog(fileno(stderr), "Initializing connection on %s:%u failed", address, port);
|
|
|
|
return FAILURE;
|
|
}
|
|
|
|
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));
|
|
|
|
phpdbg_rlog(fileno(stderr), "connection established from %s", buffer);
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
dup2(*socket, fileno(stdout));
|
|
dup2(*socket, fileno(stdin));
|
|
|
|
setbuf(stdout, NULL);
|
|
|
|
*stream = fdopen(*socket, "r+");
|
|
|
|
phpdbg_set_async_io(*socket);
|
|
#endif
|
|
return SUCCESS;
|
|
}
|
|
|
|
#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*/;
|
|
|
|
// if (!(info->si_band & POLLIN)) {
|
|
// return; /* Not interested in writeablility etc., just interested in incoming data */
|
|
// }
|
|
|
|
/* 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);
|
|
|
|
do {
|
|
char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
|
|
size_t off = 0;
|
|
|
|
if ((newlen = recv(PHPDBG_G(io)[PHPDBG_STDIN].fd, mem, PHPDBG_SIGSAFE_MEM_SIZE, MSG_PEEK)) == (size_t) -1) {
|
|
break;
|
|
}
|
|
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);
|
|
zend_try {
|
|
phpdbg_force_interruption();
|
|
} zend_end_try();
|
|
phpdbg_clear_sigsafe_mem();
|
|
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;
|
|
*/ }
|
|
}
|
|
off += i;
|
|
} while (0);
|
|
|
|
|
|
fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags);
|
|
} /* }}} */
|
|
|
|
void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
|
|
{
|
|
int is_handled = FAILURE;
|
|
|
|
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);
|
|
if (is_handled == FAILURE) {
|
|
#ifdef ZEND_SIGNALS
|
|
zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
|
|
#else
|
|
sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
|
|
} /* }}} */
|
|
#endif
|
|
|
|
static inline zend_mm_heap *phpdbg_mm_get_heap() /* {{{ */
|
|
{
|
|
zend_mm_heap *mm_heap;
|
|
|
|
|
|
mm_heap = zend_mm_set_heap(NULL);
|
|
zend_mm_set_heap(mm_heap);
|
|
|
|
return mm_heap;
|
|
} /* }}} */
|
|
|
|
void *phpdbg_malloc_wrapper(size_t size) /* {{{ */
|
|
{
|
|
return zend_mm_alloc(phpdbg_mm_get_heap(), size);
|
|
} /* }}} */
|
|
|
|
void phpdbg_free_wrapper(void *p) /* {{{ */
|
|
{
|
|
zend_mm_free(phpdbg_mm_get_heap(), p);
|
|
} /* }}} */
|
|
|
|
void *phpdbg_realloc_wrapper(void *ptr, size_t size) /* {{{ */
|
|
{
|
|
return zend_mm_realloc(phpdbg_mm_get_heap(), ptr, size);
|
|
} /* }}} */
|
|
|
|
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 = NULL;
|
|
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 = -1;
|
|
zend_bool remote = 0;
|
|
int step = 0;
|
|
zend_phpdbg_globals *settings = NULL;
|
|
char *bp_tmp = NULL;
|
|
char *address;
|
|
int listen = -1;
|
|
int server = -1;
|
|
int socket = -1;
|
|
FILE* stream = NULL;
|
|
|
|
#ifdef ZTS
|
|
void ***tsrm_ls;
|
|
#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");
|
|
#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:
|
|
ini_entries = NULL;
|
|
ini_entries_len = 0;
|
|
ini_ignore = 0;
|
|
ini_override = NULL;
|
|
zend_extensions = NULL;
|
|
zend_extensions_len = 0L;
|
|
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;
|
|
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':
|
|
phpdbg_startup_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 '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 read and write on listen port */
|
|
case 'l': /* set listen ports */
|
|
if (sscanf(php_optarg, "%d", &listen) != 1) {
|
|
listen = 8000;
|
|
}
|
|
break;
|
|
|
|
case 'a': { /* set bind address */
|
|
free(address);
|
|
if (!php_optarg) {
|
|
address = strdup("*");
|
|
} else address = strdup(php_optarg);
|
|
} break;
|
|
#endif
|
|
|
|
case 'x':
|
|
flags |= PHPDBG_WRITE_XML;
|
|
break;
|
|
|
|
case 'V': {
|
|
sapi_startup(phpdbg);
|
|
phpdbg->startup(phpdbg);
|
|
printf(
|
|
"phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) 1997-2015 The PHP Group\n%s",
|
|
PHPDBG_VERSION,
|
|
__DATE__,
|
|
__TIME__,
|
|
PHP_VERSION,
|
|
get_zend_version()
|
|
);
|
|
sapi_deactivate();
|
|
sapi_shutdown();
|
|
return 0;
|
|
} break;
|
|
}
|
|
}
|
|
|
|
/* set exec if present on command line */
|
|
if (!exec && (argc > php_optind) && (strcmp(argv[php_optind-1], "--") != SUCCESS)) {
|
|
if (strlen(argv[php_optind])) {
|
|
if (exec) {
|
|
free(exec);
|
|
}
|
|
exec = strdup(argv[php_optind]);
|
|
}
|
|
php_optind++;
|
|
}
|
|
|
|
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) {
|
|
#ifdef _WIN32
|
|
EXCEPTION_POINTERS *xp;
|
|
__try {
|
|
#endif
|
|
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 (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 <= 0 && listen > 0) {
|
|
server = phpdbg_open_socket(address, listen);
|
|
if (-1 > server || phpdbg_remote_init(address, listen, server, &socket, &stream) == 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();
|
|
|
|
phpdbg_init_list();
|
|
|
|
PHPDBG_G(original_free_function) = _free;
|
|
_free = phpdbg_watch_efree;
|
|
|
|
zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc);
|
|
|
|
phpdbg_setup_watchpoints();
|
|
|
|
#if defined(ZEND_SIGNALS) && !defined(_WIN32)
|
|
zend_try {
|
|
zend_signal_activate();
|
|
} zend_end_try();
|
|
#endif
|
|
|
|
#if defined(ZEND_SIGNALS) && !defined(_WIN32)
|
|
zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
|
|
zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
|
|
#elif !defined(_WIN32)
|
|
sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
|
|
sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
|
|
#endif
|
|
|
|
PHPDBG_G(sapi_name_ptr) = sapi_name;
|
|
|
|
php_output_activate();
|
|
php_output_deactivate();
|
|
|
|
php_output_activate();
|
|
|
|
if (php_request_startup() == SUCCESS) {
|
|
int i;
|
|
|
|
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 ? estrdup(exec) : estrdup("");
|
|
|
|
php_hash_environment();
|
|
}
|
|
|
|
/* 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) {
|
|
#endif
|
|
#if defined(ZEND_SIGNALS) && !defined(_WIN32)
|
|
zend_try { zend_signal(SIGINT, phpdbg_sigint_handler); } zend_end_try();
|
|
#else
|
|
signal(SIGINT, phpdbg_sigint_handler);
|
|
#endif
|
|
#ifndef _WIN32
|
|
}
|
|
#endif
|
|
|
|
PG(modules_activated) = 0;
|
|
|
|
#ifndef _WIN32
|
|
/* setup io here */
|
|
if (remote) {
|
|
PHPDBG_G(flags) |= PHPDBG_IS_REMOTE;
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
}
|
|
#endif
|
|
|
|
#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);
|
|
PHPDBG_G(exec_len) = PHPDBG_G(exec) ? strlen(PHPDBG_G(exec)) : 0;
|
|
|
|
free(exec);
|
|
exec = NULL;
|
|
}
|
|
|
|
if (oplog_file) { /* open oplog */
|
|
PHPDBG_G(oplog) = fopen(oplog_file, "w+");
|
|
if (!PHPDBG_G(oplog)) {
|
|
phpdbg_error("oplog", "path=\"%s\"", "Failed to open oplog %s", oplog_file);
|
|
}
|
|
free(oplog_file);
|
|
}
|
|
|
|
/* set default colors */
|
|
phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold"));
|
|
phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold"));
|
|
phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green"));
|
|
|
|
/* set default prompt */
|
|
phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT);
|
|
|
|
/* Make stdin, stdout and stderr accessible from PHP scripts */
|
|
phpdbg_register_file_handles();
|
|
|
|
if (show_banner && cleaning < 2) {
|
|
/* print blurb */
|
|
phpdbg_welcome(cleaning == 1);
|
|
}
|
|
|
|
cleaning = -1;
|
|
|
|
/* initialize from file */
|
|
PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
|
|
zend_try {
|
|
phpdbg_init(init_file, init_file_len, init_file_default);
|
|
if (bp_tmp) {
|
|
PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
|
|
phpdbg_string_init(bp_tmp);
|
|
free(bp_tmp);
|
|
bp_tmp = NULL;
|
|
PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
|
|
}
|
|
} zend_end_try();
|
|
PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
|
|
|
|
/* quit if init says so */
|
|
if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
|
|
goto phpdbg_out;
|
|
}
|
|
|
|
/* auto compile */
|
|
if (PHPDBG_G(exec)) {
|
|
if (settings) {
|
|
PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
|
|
}
|
|
phpdbg_compile();
|
|
PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
|
|
}
|
|
|
|
/* step from here, not through init */
|
|
if (step) {
|
|
PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
|
|
}
|
|
|
|
/* #ifndef for making compiler shutting up */
|
|
#ifndef _WIN32
|
|
phpdbg_interact:
|
|
#endif
|
|
|
|
/* phpdbg main() */
|
|
do {
|
|
zend_try {
|
|
if (phpdbg_startup_run) {
|
|
zend_bool quit_immediately = phpdbg_startup_run > 1;
|
|
phpdbg_startup_run = 0;
|
|
PHPDBG_COMMAND_HANDLER(run)(NULL);
|
|
if (quit_immediately) {
|
|
/* if -r is on the command line more than once just quit */
|
|
EG(bailout) = __orig_bailout; /* reset zend_try */
|
|
break;
|
|
}
|
|
}
|
|
|
|
phpdbg_interactive(1);
|
|
} zend_catch {
|
|
if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
|
|
char *bp_tmp_str;
|
|
PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
|
|
phpdbg_export_breakpoints_to_string(&bp_tmp_str);
|
|
PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
|
|
if (bp_tmp_str) {
|
|
bp_tmp = strdup(bp_tmp_str);
|
|
efree(bp_tmp_str);
|
|
}
|
|
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_remote_init(address, listen, server, &socket, &stream);
|
|
|
|
/* set streams */
|
|
if (stream) {
|
|
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 (!(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;
|
|
|
|
/* this is just helpful */
|
|
PG(report_memleaks) = 0;
|
|
|
|
#ifndef _WIN32
|
|
phpdbg_out:
|
|
if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
|
|
PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
|
|
goto phpdbg_interact;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
} __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
|
|
phpdbg_error("segfault", "", "Access violation (Segmentation fault) encountered\ntrying to abort cleanly...");
|
|
}
|
|
phpdbg_out:
|
|
#endif
|
|
|
|
if (cleaning <= 0) {
|
|
PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING;
|
|
cleaning = -1;
|
|
}
|
|
|
|
{
|
|
int i;
|
|
/* free argv */
|
|
for (i = SG(request_info).argc; --i;) {
|
|
efree(SG(request_info).argv[i]);
|
|
}
|
|
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));
|
|
#endif
|
|
if (ini_entries) {
|
|
free(ini_entries);
|
|
}
|
|
|
|
if (ini_override) {
|
|
free(ini_override);
|
|
}
|
|
|
|
/* this must be forced */
|
|
CG(unclean_shutdown) = 0;
|
|
|
|
/* this is just helpful */
|
|
PG(report_memleaks) = 0;
|
|
|
|
if ((PHPDBG_G(flags) & (PHPDBG_IS_CLEANING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_CLEANING) {
|
|
php_free_shutdown_functions();
|
|
zend_objects_store_mark_destructed(&EG(objects_store));
|
|
}
|
|
|
|
/* 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();
|
|
|
|
zend_try {
|
|
php_module_shutdown();
|
|
} zend_end_try();
|
|
|
|
sapi_shutdown();
|
|
|
|
}
|
|
|
|
if (cleaning > 0 || remote) {
|
|
goto phpdbg_main;
|
|
}
|
|
|
|
#ifdef ZTS
|
|
/* bugggy */
|
|
/* tsrm_shutdown(); */
|
|
#endif
|
|
|
|
#ifndef _WIN32
|
|
if (address) {
|
|
free(address);
|
|
}
|
|
#endif
|
|
|
|
if (PHPDBG_G(sapi_name_ptr)) {
|
|
free(PHPDBG_G(sapi_name_ptr));
|
|
}
|
|
|
|
return 0;
|
|
} /* }}} */
|