xref: /PHP-7.4/sapi/phpdbg/phpdbg.c (revision 8483a21f)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Felipe Pena <felipe@php.net>                                |
16    | Authors: Joe Watkins <joe.watkins@live.co.uk>                        |
17    | Authors: Bob Weinand <bwoebi@php.net>                                |
18    +----------------------------------------------------------------------+
19 */
20 
21 #include "phpdbg.h"
22 #include "phpdbg_prompt.h"
23 #include "phpdbg_bp.h"
24 #include "phpdbg_break.h"
25 #include "phpdbg_list.h"
26 #include "phpdbg_utils.h"
27 #include "phpdbg_set.h"
28 #include "phpdbg_io.h"
29 #include "zend_alloc.h"
30 #include "phpdbg_eol.h"
31 #include "phpdbg_print.h"
32 #include "phpdbg_help.h"
33 
34 #include "ext/standard/basic_functions.h"
35 
36 /* {{{ remote console headers */
37 #ifndef _WIN32
38 #	include <sys/socket.h>
39 #	include <sys/select.h>
40 #	include <sys/time.h>
41 #	include <sys/types.h>
42 #	if HAVE_POLL_H
43 #		include <poll.h>
44 #	elif HAVE_SYS_POLL_H
45 #		include <sys/poll.h>
46 #	endif
47 #	include <netinet/in.h>
48 #	include <unistd.h>
49 #	include <arpa/inet.h>
50 #endif /* }}} */
51 
52 #if defined(PHP_WIN32) && defined(HAVE_OPENSSL)
53 # include "openssl/applink.c"
54 #endif
55 
56 #if defined(PHP_WIN32) && defined(ZTS)
57 ZEND_TSRMLS_CACHE_DEFINE()
58 #endif
59 
60 ZEND_DECLARE_MODULE_GLOBALS(phpdbg);
61 int phpdbg_startup_run = 0;
62 
PHP_INI_MH(OnUpdateEol)63 static PHP_INI_MH(OnUpdateEol)
64 {
65 	if (!new_value) {
66 		return FAILURE;
67 	}
68 
69 	return phpdbg_eol_global_update(ZSTR_VAL(new_value));
70 }
71 
72 PHP_INI_BEGIN()
73 	STD_PHP_INI_ENTRY("phpdbg.path", "", PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString, socket_path, zend_phpdbg_globals, phpdbg_globals)
74 	STD_PHP_INI_ENTRY("phpdbg.eol", "2", PHP_INI_ALL, OnUpdateEol, eol, zend_phpdbg_globals, phpdbg_globals)
75 PHP_INI_END()
76 
77 static zend_bool phpdbg_booted = 0;
78 static zend_bool phpdbg_fully_started = 0;
79 zend_bool use_mm_wrappers = 1;
80 
php_phpdbg_destroy_bp_file(zval * brake)81 static void php_phpdbg_destroy_bp_file(zval *brake) /* {{{ */
82 {
83 	zend_hash_destroy(Z_ARRVAL_P(brake));
84 	efree(Z_ARRVAL_P(brake));
85 } /* }}} */
86 
php_phpdbg_destroy_bp_symbol(zval * brake)87 static void php_phpdbg_destroy_bp_symbol(zval *brake) /* {{{ */
88 {
89 	efree((char *) ((phpdbg_breaksymbol_t *) Z_PTR_P(brake))->symbol);
90 	efree(Z_PTR_P(brake));
91 } /* }}} */
92 
php_phpdbg_destroy_bp_opcode(zval * brake)93 static void php_phpdbg_destroy_bp_opcode(zval *brake) /* {{{ */
94 {
95 	efree((char *) ((phpdbg_breakop_t *) Z_PTR_P(brake))->name);
96 	efree(Z_PTR_P(brake));
97 } /* }}} */
98 
php_phpdbg_destroy_bp_opline(zval * brake)99 static void php_phpdbg_destroy_bp_opline(zval *brake) /* {{{ */
100 {
101 	efree(Z_PTR_P(brake));
102 } /* }}} */
103 
php_phpdbg_destroy_bp_methods(zval * brake)104 static void php_phpdbg_destroy_bp_methods(zval *brake) /* {{{ */
105 {
106 	zend_hash_destroy(Z_ARRVAL_P(brake));
107 	efree(Z_ARRVAL_P(brake));
108 } /* }}} */
109 
php_phpdbg_destroy_bp_condition(zval * data)110 static void php_phpdbg_destroy_bp_condition(zval *data) /* {{{ */
111 {
112 	phpdbg_breakcond_t *brake = (phpdbg_breakcond_t *) Z_PTR_P(data);
113 
114 	if (brake->ops) {
115 		destroy_op_array(brake->ops);
116 		efree(brake->ops);
117 	}
118 	efree((char*) brake->code);
119 	efree(brake);
120 } /* }}} */
121 
php_phpdbg_destroy_registered(zval * data)122 static void php_phpdbg_destroy_registered(zval *data) /* {{{ */
123 {
124 	zend_function_dtor(data);
125 } /* }}} */
126 
php_phpdbg_destroy_file_source(zval * data)127 static void php_phpdbg_destroy_file_source(zval *data) /* {{{ */
128 {
129 	phpdbg_file_source *source = (phpdbg_file_source *) Z_PTR_P(data);
130 	destroy_op_array(&source->op_array);
131 	if (source->buf) {
132 		efree(source->buf);
133 	}
134 	efree(source);
135 } /* }}} */
136 
php_phpdbg_globals_ctor(zend_phpdbg_globals * pg)137 static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
138 {
139 	pg->prompt[0] = NULL;
140 	pg->prompt[1] = NULL;
141 
142 	pg->colors[0] = NULL;
143 	pg->colors[1] = NULL;
144 	pg->colors[2] = NULL;
145 
146 	pg->lines = phpdbg_get_terminal_height();
147 	pg->exec = NULL;
148 	pg->exec_len = 0;
149 	pg->buffer = NULL;
150 	pg->last_was_newline = 1;
151 	pg->ops = NULL;
152 	pg->vmret = 0;
153 	pg->in_execution = 0;
154 	pg->bp_count = 0;
155 	pg->flags = PHPDBG_DEFAULT_FLAGS;
156 	pg->oplog = NULL;
157 	memset(pg->io, 0, sizeof(pg->io));
158 	pg->frame.num = 0;
159 	pg->sapi_name_ptr = NULL;
160 	pg->socket_fd = -1;
161 	pg->socket_server_fd = -1;
162 	pg->unclean_eval = 0;
163 
164 	pg->req_id = 0;
165 	pg->err_buf.active = 0;
166 	pg->err_buf.type = 0;
167 
168 	pg->input_buflen = 0;
169 	pg->sigsafe_mem.mem = NULL;
170 	pg->sigsegv_bailout = NULL;
171 
172 	pg->oplog_list = NULL;
173 
174 #ifdef PHP_WIN32
175 	pg->sigio_watcher_thread = INVALID_HANDLE_VALUE;
176 	memset(&pg->swd, 0, sizeof(struct win32_sigio_watcher_data));
177 #endif
178 
179 	pg->eol = PHPDBG_EOL_LF;
180 
181 	pg->stdin_file = NULL;
182 
183 	pg->cur_command = NULL;
184 	pg->last_line = 0;
185 } /* }}} */
186 
PHP_MINIT_FUNCTION(phpdbg)187 static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
188 {
189 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
190 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], 8, NULL, php_phpdbg_destroy_bp_file, 0);
191 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
192 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
193 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
194 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
195 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, php_phpdbg_destroy_bp_opline, 0);
196 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0);
197 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
198 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
199 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
200 
201 	zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
202 	zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
203 
204 	zend_hash_init(&PHPDBG_G(file_sources), 0, NULL, php_phpdbg_destroy_file_source, 0);
205 	phpdbg_setup_watchpoints();
206 
207 	REGISTER_INI_ENTRIES();
208 
209 	zend_execute_ex = phpdbg_execute_ex;
210 
211 	REGISTER_STRINGL_CONSTANT("PHPDBG_VERSION", PHPDBG_VERSION, sizeof(PHPDBG_VERSION)-1, CONST_CS|CONST_PERSISTENT);
212 
213 	REGISTER_LONG_CONSTANT("PHPDBG_COLOR_PROMPT", PHPDBG_COLOR_PROMPT, CONST_CS|CONST_PERSISTENT);
214 	REGISTER_LONG_CONSTANT("PHPDBG_COLOR_NOTICE", PHPDBG_COLOR_NOTICE, CONST_CS|CONST_PERSISTENT);
215 	REGISTER_LONG_CONSTANT("PHPDBG_COLOR_ERROR",  PHPDBG_COLOR_ERROR, CONST_CS|CONST_PERSISTENT);
216 
217 	return SUCCESS;
218 } /* }}} */
219 
PHP_MSHUTDOWN_FUNCTION(phpdbg)220 static PHP_MSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
221 {
222 	zend_hash_destroy(&PHPDBG_G(registered));
223 	phpdbg_destroy_watchpoints();
224 
225 	if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
226 		phpdbg_notice("stop", "type=\"normal\"", "Script ended normally");
227 	}
228 
229 	/* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */
230 	if (use_mm_wrappers) {
231 		/* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */
232 		*(int *) zend_mm_get_heap() = 0;
233 	}
234 
235 	if (PHPDBG_G(buffer)) {
236 		free(PHPDBG_G(buffer));
237 		PHPDBG_G(buffer) = NULL;
238 	}
239 
240 	if (PHPDBG_G(exec)) {
241 		free(PHPDBG_G(exec));
242 		PHPDBG_G(exec) = NULL;
243 	}
244 
245 	if (PHPDBG_G(oplog)) {
246 		fclose(PHPDBG_G(oplog));
247 		PHPDBG_G(oplog) = NULL;
248 	}
249 
250 	if (PHPDBG_G(oplog_list)) {
251 		phpdbg_oplog_list *cur = PHPDBG_G(oplog_list);
252 		do {
253 			phpdbg_oplog_list *prev = cur->prev;
254 			efree(cur);
255 			cur = prev;
256 		} while (cur != NULL);
257 
258 		zend_arena_destroy(PHPDBG_G(oplog_arena));
259 		PHPDBG_G(oplog_list) = NULL;
260 	}
261 
262 	fflush(stdout);
263 	if (SG(request_info).argv0) {
264 		free(SG(request_info).argv0);
265 		SG(request_info).argv0 = NULL;
266 	}
267 
268 	return SUCCESS;
269 }
270 /* }}} */
271 
PHP_RINIT_FUNCTION(phpdbg)272 static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
273 {
274 	/* deactivate symbol table caching to have these properly destroyed upon stack leaving (especially important for watchpoints) */
275 	EG(symtable_cache_limit) = EG(symtable_cache);
276 
277 	return SUCCESS;
278 } /* }}} */
279 
PHP_RSHUTDOWN_FUNCTION(phpdbg)280 static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
281 {
282 	if (PHPDBG_G(stdin_file)) {
283 		fclose(PHPDBG_G(stdin_file));
284 		PHPDBG_G(stdin_file) = NULL;
285 	}
286 
287 	return SUCCESS;
288 } /* }}} */
289 
290 /* {{{ proto mixed phpdbg_exec(string context)
291 	Attempt to set the execution context for phpdbg
292 	If the execution context was set previously it is returned
293 	If the execution context was not set previously boolean true is returned
294 	If the request to set the context fails, boolean false is returned, and an E_WARNING raised */
PHP_FUNCTION(phpdbg_exec)295 static PHP_FUNCTION(phpdbg_exec)
296 {
297 	zend_string *exec;
298 
299 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &exec) == FAILURE) {
300 		return;
301 	}
302 
303 	{
304 		zend_stat_t sb;
305 		zend_bool result = 1;
306 
307 		if (VCWD_STAT(ZSTR_VAL(exec), &sb) != FAILURE) {
308 			if (sb.st_mode & (S_IFREG|S_IFLNK)) {
309 				if (PHPDBG_G(exec)) {
310 					ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len));
311 					free(PHPDBG_G(exec));
312 					result = 0;
313 				}
314 
315 				PHPDBG_G(exec) = zend_strndup(ZSTR_VAL(exec), ZSTR_LEN(exec));
316 				PHPDBG_G(exec_len) = ZSTR_LEN(exec);
317 
318 				if (result) {
319 					ZVAL_TRUE(return_value);
320 				}
321 			} else {
322 				zend_error(E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", ZSTR_VAL(exec));
323 				ZVAL_FALSE(return_value);
324 			}
325 		} else {
326 			zend_error(E_WARNING, "Failed to set execution context (%s) the file does not exist", ZSTR_VAL(exec));
327 
328 			ZVAL_FALSE(return_value);
329 		}
330 	}
331 } /* }}} */
332 
333 /* {{{ proto void phpdbg_break()
334     instructs phpdbg to insert a breakpoint at the next opcode */
PHP_FUNCTION(phpdbg_break_next)335 static PHP_FUNCTION(phpdbg_break_next)
336 {
337 	zend_execute_data *ex = EG(current_execute_data);
338 
339 	while (ex && ex->func && !ZEND_USER_CODE(ex->func->type)) {
340 		ex = ex->prev_execute_data;
341 	}
342 
343 	if (zend_parse_parameters_none() == FAILURE || !ex) {
344 		return;
345 	}
346 
347 	phpdbg_set_breakpoint_opline_ex((phpdbg_opline_ptr_t) ex->opline + 1);
348 } /* }}} */
349 
350 /* {{{ proto void phpdbg_break_file(string file, int line) */
PHP_FUNCTION(phpdbg_break_file)351 static PHP_FUNCTION(phpdbg_break_file)
352 {
353 	char *file;
354 	size_t flen;
355 	zend_long line;
356 
357 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &file, &flen, &line) == FAILURE) {
358 		return;
359 	}
360 
361 	phpdbg_set_breakpoint_file(file, 0, line);
362 } /* }}} */
363 
364 /* {{{ proto void phpdbg_break_method(string class, string method) */
PHP_FUNCTION(phpdbg_break_method)365 static PHP_FUNCTION(phpdbg_break_method)
366 {
367 	char *class = NULL, *method = NULL;
368 	size_t clen = 0, mlen = 0;
369 
370 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &class, &clen, &method, &mlen) == FAILURE) {
371 		return;
372 	}
373 
374 	phpdbg_set_breakpoint_method(class, method);
375 } /* }}} */
376 
377 /* {{{ proto void phpdbg_break_function(string function) */
PHP_FUNCTION(phpdbg_break_function)378 static PHP_FUNCTION(phpdbg_break_function)
379 {
380 	char    *function = NULL;
381 	size_t   function_len;
382 
383 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &function, &function_len) == FAILURE) {
384 		return;
385 	}
386 
387 	phpdbg_set_breakpoint_symbol(function, function_len);
388 } /* }}} */
389 
390 /* {{{ proto void phpdbg_clear(void)
391    instructs phpdbg to clear breakpoints */
PHP_FUNCTION(phpdbg_clear)392 static PHP_FUNCTION(phpdbg_clear)
393 {
394 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
395 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
396 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
397 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
398 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
399 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
400 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
401 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
402 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
403 } /* }}} */
404 
405 /* {{{ proto void phpdbg_color(int element, string color) */
PHP_FUNCTION(phpdbg_color)406 static PHP_FUNCTION(phpdbg_color)
407 {
408 	zend_long element;
409 	char *color;
410 	size_t color_len;
411 
412 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &element, &color, &color_len) == FAILURE) {
413 		return;
414 	}
415 
416 	switch (element) {
417 		case PHPDBG_COLOR_NOTICE:
418 		case PHPDBG_COLOR_ERROR:
419 		case PHPDBG_COLOR_PROMPT:
420 			phpdbg_set_color_ex(element, color, color_len);
421 		break;
422 
423 		default: zend_error(E_ERROR, "phpdbg detected an incorrect color constant");
424 	}
425 } /* }}} */
426 
427 /* {{{ proto void phpdbg_prompt(string prompt) */
PHP_FUNCTION(phpdbg_prompt)428 static PHP_FUNCTION(phpdbg_prompt)
429 {
430 	char *prompt = NULL;
431 	size_t prompt_len = 0;
432 
433 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &prompt, &prompt_len) == FAILURE) {
434 		return;
435 	}
436 
437 	phpdbg_set_prompt(prompt);
438 } /* }}} */
439 
440 /* {{{ proto void phpdbg_start_oplog() */
PHP_FUNCTION(phpdbg_start_oplog)441 static PHP_FUNCTION(phpdbg_start_oplog)
442 {
443 	phpdbg_oplog_list *prev;
444 
445 	if (zend_parse_parameters_none() == FAILURE) {
446 		return;
447 	}
448 
449 	prev = PHPDBG_G(oplog_list);
450 
451 	if (!prev) {
452 		PHPDBG_G(oplog_arena) = zend_arena_create(64 * 1024);
453 	}
454 
455 	PHPDBG_G(oplog_list) = emalloc(sizeof(phpdbg_oplog_list));
456 	PHPDBG_G(oplog_list)->prev = prev;
457 	PHPDBG_G(oplog_cur) = &PHPDBG_G(oplog_list)->start;
458 	PHPDBG_G(oplog_cur)->next = NULL;
459 }
460 
phpdbg_is_ignored_opcode(zend_uchar opcode)461 static zend_always_inline zend_bool phpdbg_is_ignored_opcode(zend_uchar opcode) {
462 	return
463 	    opcode == ZEND_NOP || opcode == ZEND_OP_DATA || opcode == ZEND_FE_FREE || opcode == ZEND_FREE || opcode == ZEND_ASSERT_CHECK || opcode == ZEND_VERIFY_RETURN_TYPE
464 	 || opcode == ZEND_DECLARE_CONST || opcode == ZEND_DECLARE_CLASS || opcode == ZEND_DECLARE_FUNCTION
465 	 || opcode == ZEND_DECLARE_CLASS_DELAYED
466 	 || opcode == ZEND_DECLARE_ANON_CLASS || opcode == ZEND_FAST_RET || opcode == ZEND_TICKS
467 	 || opcode == ZEND_EXT_STMT || opcode == ZEND_EXT_FCALL_BEGIN || opcode == ZEND_EXT_FCALL_END || opcode == ZEND_EXT_NOP || opcode == ZEND_BIND_GLOBAL
468 	;
469 }
470 
phpdbg_oplog_fill_executable(zend_op_array * op_array,HashTable * insert_ht,zend_bool by_opcode)471 static void phpdbg_oplog_fill_executable(zend_op_array *op_array, HashTable *insert_ht, zend_bool by_opcode) {
472 	/* ignore RECV_* opcodes */
473 	zend_op *cur = op_array->opcodes + op_array->num_args + !!(op_array->fn_flags & ZEND_ACC_VARIADIC);
474 	zend_op *end = op_array->opcodes + op_array->last;
475 
476 	zend_long insert_idx;
477 	zval zero;
478 	ZVAL_LONG(&zero, 0);
479 
480 	/* ignore autogenerated return (well, not too precise with finally branches, but that's okay) */
481 	if (op_array->last >= 1 && (((end - 1)->opcode == ZEND_RETURN || (end - 1)->opcode == ZEND_RETURN_BY_REF || (end - 1)->opcode == ZEND_GENERATOR_RETURN)
482 	 && ((op_array->last > 1 && ((end - 2)->opcode == ZEND_RETURN || (end - 2)->opcode == ZEND_RETURN_BY_REF || (end - 2)->opcode == ZEND_GENERATOR_RETURN || (end - 2)->opcode == ZEND_THROW))
483 	  || op_array->function_name == NULL || (end - 1)->extended_value == -1))) {
484 		end--;
485 	}
486 
487 	for (; cur < end; cur++) {
488 		zend_uchar opcode = cur->opcode;
489 		if (phpdbg_is_ignored_opcode(opcode)) {
490 			continue;
491 		}
492 
493 		if (by_opcode) {
494 			insert_idx = cur - op_array->opcodes;
495 		} else {
496 			insert_idx = cur->lineno;
497 		}
498 
499 		if (opcode == ZEND_NEW && cur[1].opcode == ZEND_DO_FCALL) {
500 			cur++;
501 		}
502 
503 		zend_hash_index_update(insert_ht, insert_idx, &zero);
504 	}
505 }
506 
phpdbg_add_empty_array(HashTable * ht,zend_string * name)507 static inline HashTable* phpdbg_add_empty_array(HashTable *ht, zend_string *name) {
508 	zval *ht_zv = zend_hash_find(ht, name);
509 	if (!ht_zv) {
510 		zval zv;
511 		array_init(&zv);
512 		ht_zv = zend_hash_add_new(ht, name, &zv);
513 	}
514 	return Z_ARR_P(ht_zv);
515 }
516 
517 /* {{{ proto void phpdbg_get_executable() */
PHP_FUNCTION(phpdbg_get_executable)518 static PHP_FUNCTION(phpdbg_get_executable)
519 {
520 	HashTable *options = NULL;
521 	zval *option_buffer;
522 	zend_bool by_function = 0;
523 	zend_bool by_opcode = 0;
524 	HashTable *insert_ht;
525 
526 	zend_function *func;
527 	zend_class_entry *ce;
528 	zend_string *name;
529 	HashTable *files = &PHPDBG_G(file_sources);
530 	HashTable files_tmp;
531 
532 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) {
533 		return;
534 	}
535 
536 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("functions")))) {
537 		by_function = zend_is_true(option_buffer);
538 	}
539 
540 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("opcodes")))) {
541 		if (by_function) {
542 			by_opcode = zend_is_true(option_buffer);
543 		}
544 	}
545 
546 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("files")))) {
547 		ZVAL_DEREF(option_buffer);
548 		if (Z_TYPE_P(option_buffer) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(option_buffer)) > 0) {
549 			zval *filename;
550 
551 			files = &files_tmp;
552 			zend_hash_init(files, 0, NULL, NULL, 0);
553 
554 			ZEND_HASH_FOREACH_VAL(Z_ARR_P(option_buffer), filename) {
555 				zend_hash_add_empty_element(files, zval_get_string(filename));
556 			} ZEND_HASH_FOREACH_END();
557 		} else {
558 			GC_ADDREF(files);
559 		}
560 	} else {
561 		GC_ADDREF(files);
562 	}
563 
564 	array_init(return_value);
565 
566 	ZEND_HASH_FOREACH_STR_KEY_PTR(EG(function_table), name, func) {
567 		if (func->type == ZEND_USER_FUNCTION) {
568 			if (zend_hash_exists(files, func->op_array.filename)) {
569 				insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), func->op_array.filename);
570 
571 				if (by_function) {
572 					insert_ht = phpdbg_add_empty_array(insert_ht, name);
573 				}
574 
575 				phpdbg_oplog_fill_executable(&func->op_array, insert_ht, by_opcode);
576 			}
577 		}
578 	} ZEND_HASH_FOREACH_END();
579 
580 	ZEND_HASH_FOREACH_STR_KEY_PTR(EG(class_table), name, ce) {
581 		if (ce->type == ZEND_USER_CLASS) {
582 			if (zend_hash_exists(files, ce->info.user.filename)) {
583 				ZEND_HASH_FOREACH_PTR(&ce->function_table, func) {
584 					if (func->type == ZEND_USER_FUNCTION && zend_hash_exists(files, func->op_array.filename)) {
585 						insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), func->op_array.filename);
586 
587 						if (by_function) {
588 							zend_string *fn_name = strpprintf(ZSTR_LEN(name) + ZSTR_LEN(func->op_array.function_name) + 2, "%.*s::%.*s", (int) ZSTR_LEN(name), ZSTR_VAL(name), (int) ZSTR_LEN(func->op_array.function_name), ZSTR_VAL(func->op_array.function_name));
589 							insert_ht = phpdbg_add_empty_array(insert_ht, fn_name);
590 							zend_string_release(fn_name);
591 						}
592 
593 						phpdbg_oplog_fill_executable(&func->op_array, insert_ht, by_opcode);
594 					}
595 				} ZEND_HASH_FOREACH_END();
596 			}
597 		}
598 	} ZEND_HASH_FOREACH_END();
599 
600 	ZEND_HASH_FOREACH_STR_KEY(files, name) {
601 		phpdbg_file_source *source = zend_hash_find_ptr(&PHPDBG_G(file_sources), name);
602 		if (source) {
603 			phpdbg_oplog_fill_executable(
604 				&source->op_array,
605 				phpdbg_add_empty_array(Z_ARR_P(return_value), source->op_array.filename),
606 				by_opcode);
607 		}
608 	} ZEND_HASH_FOREACH_END();
609 
610 	if (!GC_DELREF(files)) {
611 		zend_hash_destroy(files);
612 	}
613 }
614 
615 /* {{{ proto void phpdbg_end_oplog() */
PHP_FUNCTION(phpdbg_end_oplog)616 static PHP_FUNCTION(phpdbg_end_oplog)
617 {
618 	phpdbg_oplog_entry *cur;
619 	phpdbg_oplog_list *prev;
620 
621 	HashTable *options = NULL;
622 	zval *option_buffer;
623 	zend_bool by_function = 0;
624 	zend_bool by_opcode = 0;
625 
626 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) {
627 		return;
628 	}
629 
630 	if (!PHPDBG_G(oplog_list)) {
631 		zend_error(E_WARNING, "Can not end an oplog without starting it");
632 		return;
633 	}
634 
635 	cur = PHPDBG_G(oplog_list)->start.next;
636 	prev = PHPDBG_G(oplog_list)->prev;
637 
638 	efree(PHPDBG_G(oplog_list));
639 	PHPDBG_G(oplog_list) = prev;
640 
641 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("functions")))) {
642 		by_function = zend_is_true(option_buffer);
643 	}
644 
645 	if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("opcodes")))) {
646 		if (by_function) {
647 			by_opcode = zend_is_true(option_buffer);
648 		}
649 	}
650 
651 	array_init(return_value);
652 
653 	{
654 		zend_string *last_file = NULL;
655 		HashTable *file_ht = NULL;
656 		zend_string *last_function = (void *)~(uintptr_t)0;
657 		zend_class_entry *last_scope = NULL;
658 
659 		HashTable *insert_ht = NULL;
660 		zend_long insert_idx;
661 
662 		do {
663 			zval zero;
664 			ZVAL_LONG(&zero, 0);
665 
666 			if (cur->filename != last_file) {
667 				last_file = cur->filename;
668 				file_ht = insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), last_file);
669 			}
670 
671 			if (by_function) {
672 				if (cur->function_name == NULL) {
673 					if (last_function != NULL) {
674 						insert_ht = file_ht;
675 					}
676 					last_function = NULL;
677 				} else if (cur->function_name != last_function || cur->scope != last_scope) {
678 					zend_string *fn_name;
679 					last_function = cur->function_name;
680 					last_scope = cur->scope;
681 					if (last_scope == NULL) {
682 						fn_name = zend_string_copy(last_function);
683 					} else {
684 						fn_name = strpprintf(ZSTR_LEN(last_function) + ZSTR_LEN(last_scope->name) + 2, "%.*s::%.*s", (int) ZSTR_LEN(last_scope->name), ZSTR_VAL(last_scope->name), (int) ZSTR_LEN(last_function), ZSTR_VAL(last_function));
685 					}
686 					insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), fn_name);
687 					zend_string_release(fn_name);
688 				}
689 			}
690 
691 			if (by_opcode) {
692 				insert_idx = cur->op - cur->opcodes;
693 			} else {
694 				if (phpdbg_is_ignored_opcode(cur->op->opcode)) {
695 					continue;
696 				}
697 
698 				insert_idx = cur->op->lineno;
699 			}
700 
701 			ZEND_ASSERT(insert_ht && file_ht);
702 			{
703 				zval *num = zend_hash_index_find(insert_ht, insert_idx);
704 				if (!num) {
705 					num = zend_hash_index_add_new(insert_ht, insert_idx, &zero);
706 				}
707 				Z_LVAL_P(num)++;
708 			}
709 
710 		} while ((cur = cur->next));
711 	}
712 
713 	if (!prev) {
714 		zend_arena_destroy(PHPDBG_G(oplog_arena));
715 	}
716 }
717 
718 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_next_arginfo, 0, 0, 0)
719 ZEND_END_ARG_INFO()
720 
721 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_file_arginfo, 0, 0, 2)
722 	ZEND_ARG_INFO(0, file)
723 	ZEND_ARG_INFO(0, line)
724 ZEND_END_ARG_INFO()
725 
726 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_method_arginfo, 0, 0, 2)
727 	ZEND_ARG_INFO(0, class)
728 	ZEND_ARG_INFO(0, method)
729 ZEND_END_ARG_INFO()
730 
731 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_function_arginfo, 0, 0, 1)
732 	ZEND_ARG_INFO(0, function)
733 ZEND_END_ARG_INFO()
734 
735 ZEND_BEGIN_ARG_INFO_EX(phpdbg_color_arginfo, 0, 0, 2)
736 	ZEND_ARG_INFO(0, element)
737 	ZEND_ARG_INFO(0, color)
738 ZEND_END_ARG_INFO()
739 
740 ZEND_BEGIN_ARG_INFO_EX(phpdbg_prompt_arginfo, 0, 0, 1)
741 	ZEND_ARG_INFO(0, string)
742 ZEND_END_ARG_INFO()
743 
744 ZEND_BEGIN_ARG_INFO_EX(phpdbg_exec_arginfo, 0, 0, 1)
745 	ZEND_ARG_INFO(0, context)
746 ZEND_END_ARG_INFO()
747 
748 ZEND_BEGIN_ARG_INFO_EX(phpdbg_clear_arginfo, 0, 0, 0)
749 ZEND_END_ARG_INFO()
750 
751 ZEND_BEGIN_ARG_INFO_EX(phpdbg_start_oplog_arginfo, 0, 0, 0)
752 ZEND_END_ARG_INFO()
753 
754 ZEND_BEGIN_ARG_INFO_EX(phpdbg_end_oplog_arginfo, 0, 0, 0)
755 	ZEND_ARG_INFO(0, options)
756 ZEND_END_ARG_INFO()
757 
758 ZEND_BEGIN_ARG_INFO_EX(phpdbg_get_executable_arginfo, 0, 0, 0)
759 	ZEND_ARG_INFO(0, options)
760 ZEND_END_ARG_INFO()
761 
762 static const zend_function_entry phpdbg_user_functions[] = {
763 	PHP_FE(phpdbg_clear, phpdbg_clear_arginfo)
764 	PHP_FE(phpdbg_break_next, phpdbg_break_next_arginfo)
765 	PHP_FE(phpdbg_break_file, phpdbg_break_file_arginfo)
766 	PHP_FE(phpdbg_break_method, phpdbg_break_method_arginfo)
767 	PHP_FE(phpdbg_break_function, phpdbg_break_function_arginfo)
768 	PHP_FE(phpdbg_exec,  phpdbg_exec_arginfo)
769 	PHP_FE(phpdbg_color, phpdbg_color_arginfo)
770 	PHP_FE(phpdbg_prompt, phpdbg_prompt_arginfo)
771 	PHP_FE(phpdbg_start_oplog, phpdbg_start_oplog_arginfo)
772 	PHP_FE(phpdbg_end_oplog, phpdbg_end_oplog_arginfo)
773 	PHP_FE(phpdbg_get_executable, phpdbg_get_executable_arginfo)
774 #ifdef  PHP_FE_END
775 	PHP_FE_END
776 #else
777 	{NULL,NULL,NULL}
778 #endif
779 };
780 
781 static zend_module_entry sapi_phpdbg_module_entry = {
782 	STANDARD_MODULE_HEADER,
783 	PHPDBG_NAME,
784 	phpdbg_user_functions,
785 	PHP_MINIT(phpdbg),
786 	PHP_MSHUTDOWN(phpdbg),
787 	PHP_RINIT(phpdbg),
788 	PHP_RSHUTDOWN(phpdbg),
789 	NULL,
790 	PHPDBG_VERSION,
791 	STANDARD_MODULE_PROPERTIES
792 };
793 
php_sapi_phpdbg_module_startup(sapi_module_struct * module)794 static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */
795 {
796 	if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) {
797 		return FAILURE;
798 	}
799 
800 	phpdbg_booted = 1;
801 
802 	return SUCCESS;
803 } /* }}} */
804 
php_sapi_phpdbg_read_cookies(void)805 static char* php_sapi_phpdbg_read_cookies(void) /* {{{ */
806 {
807 	return NULL;
808 } /* }}} */
809 
php_sapi_phpdbg_header_handler(sapi_header_struct * h,sapi_header_op_enum op,sapi_headers_struct * s)810 static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s) /* {{{ */
811 {
812 	return 0;
813 }
814 /* }}} */
815 
php_sapi_phpdbg_send_headers(sapi_headers_struct * sapi_headers)816 static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */
817 {
818 	/* We do nothing here, this function is needed to prevent that the fallback
819 	 * header handling is called. */
820 	return SAPI_HEADER_SENT_SUCCESSFULLY;
821 }
822 /* }}} */
823 
php_sapi_phpdbg_send_header(sapi_header_struct * sapi_header,void * server_context)824 static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context) /* {{{ */
825 {
826 }
827 /* }}} */
828 
php_sapi_phpdbg_log_message(char * message,int syslog_type_int)829 static void php_sapi_phpdbg_log_message(char *message, int syslog_type_int) /* {{{ */
830 {
831 	/*
832 	* We must not request TSRM before being booted
833 	*/
834 	if (phpdbg_booted) {
835 		if (PHPDBG_G(flags) & PHPDBG_IN_EVAL) {
836 			phpdbg_error("eval", "msg=\"%s\"", "%s", message);
837 			return;
838 		}
839 
840 		phpdbg_error("php", "msg=\"%s\"", "%s", message);
841 
842 		if (PHPDBG_G(flags) & PHPDBG_PREVENT_INTERACTIVE) {
843 			return;
844 		}
845 
846 		switch (PG(last_error_type)) {
847 			case E_ERROR:
848 			case E_CORE_ERROR:
849 			case E_COMPILE_ERROR:
850 			case E_USER_ERROR:
851 			case E_PARSE:
852 			case E_RECOVERABLE_ERROR: {
853 				const char *file_char = zend_get_executed_filename();
854 				zend_string *file = zend_string_init(file_char, strlen(file_char), 0);
855 				phpdbg_list_file(file, 3, zend_get_executed_lineno() - 1, zend_get_executed_lineno());
856 				zend_string_release(file);
857 
858 				if (!phpdbg_fully_started) {
859 					return;
860 				}
861 
862 				do {
863 					switch (phpdbg_interactive(1, NULL)) {
864 						case PHPDBG_LEAVE:
865 						case PHPDBG_FINISH:
866 						case PHPDBG_UNTIL:
867 						case PHPDBG_NEXT:
868 							return;
869 					}
870 				} while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
871 			}
872 		}
873 	} else {
874 		fprintf(stdout, "%s\n", message);
875 	}
876 }
877 /* }}} */
878 
php_sapi_phpdbg_activate(void)879 static int php_sapi_phpdbg_activate(void) /* {{{ */
880 {
881 	return SUCCESS;
882 }
883 
php_sapi_phpdbg_deactivate(void)884 static int php_sapi_phpdbg_deactivate(void) /* {{{ */
885 {
886 	/* Everything using ZMM should be freed here... */
887 	zend_hash_destroy(&PHPDBG_G(file_sources));
888 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
889 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
890 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
891 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
892 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
893 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
894 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
895 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
896 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
897 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
898 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
899 	zend_hash_destroy(&PHPDBG_G(seek));
900 
901 	if (PHPDBG_G(ops)) {
902 		destroy_op_array(PHPDBG_G(ops));
903 		efree(PHPDBG_G(ops));
904 		PHPDBG_G(ops) = NULL;
905 	}
906 
907 	return SUCCESS;
908 }
909 
php_sapi_phpdbg_register_vars(zval * track_vars_array)910 static void php_sapi_phpdbg_register_vars(zval *track_vars_array) /* {{{ */
911 {
912 	size_t len;
913 	char  *docroot = "";
914 
915 	/* In phpdbg mode, we consider the environment to be a part of the server variables
916 	*/
917 	php_import_environment_variables(track_vars_array);
918 
919 	if (PHPDBG_G(exec)) {
920 		len = PHPDBG_G(exec_len);
921 		if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
922 			php_register_variable("PHP_SELF", PHPDBG_G(exec), track_vars_array);
923 		}
924 		if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
925 			php_register_variable("SCRIPT_NAME", PHPDBG_G(exec), track_vars_array);
926 		}
927 
928 		if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
929 			php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec), track_vars_array);
930 		}
931 		if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) {
932 			php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec), track_vars_array);
933 		}
934 	}
935 
936 	/* any old docroot will do */
937 	len = 0;
938 	if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", &docroot, len, &len)) {
939 		php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array);
940 	}
941 }
942 /* }}} */
943 
php_sapi_phpdbg_ub_write(const char * message,size_t length)944 static inline size_t php_sapi_phpdbg_ub_write(const char *message, size_t length) /* {{{ */
945 {
946 	if (PHPDBG_G(socket_fd) != -1 && !(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
947 		send(PHPDBG_G(socket_fd), message, length, 0);
948 	}
949 	return phpdbg_script(P_STDOUT, "%.*s", (int) length, message);
950 } /* }}} */
951 
952 /* beginning of struct, see main/streams/plain_wrapper.c line 111 */
953 typedef struct {
954 	FILE *file;
955 	int fd;
956 } php_stdio_stream_data;
957 
phpdbg_stdiop_write(php_stream * stream,const char * buf,size_t count)958 static ssize_t phpdbg_stdiop_write(php_stream *stream, const char *buf, size_t count) {
959 	php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
960 
961 	while (data->fd >= 0) {
962 		struct stat stat[3];
963 		memset(stat, 0, sizeof(stat));
964 		if (((fstat(fileno(stderr), &stat[2]) < 0) & (fstat(fileno(stdout), &stat[0]) < 0)) | (fstat(data->fd, &stat[1]) < 0)) {
965 			break;
966 		}
967 
968 		if (stat[0].st_dev == stat[1].st_dev && stat[0].st_ino == stat[1].st_ino) {
969 			phpdbg_script(P_STDOUT, "%.*s", (int) count, buf);
970 			return count;
971 		}
972 		if (stat[2].st_dev == stat[1].st_dev && stat[2].st_ino == stat[1].st_ino) {
973 			phpdbg_script_ex(PHPDBG_G(io)[PHPDBG_STDERR].fd, P_STDERR, "%.*s", (int) count, buf);
974 			return count;
975 		}
976 		break;
977 	}
978 
979 	return PHPDBG_G(php_stdiop_write)(stream, buf, count);
980 }
981 
php_sapi_phpdbg_flush(void * context)982 static inline void php_sapi_phpdbg_flush(void *context)  /* {{{ */
983 {
984 	if (!phpdbg_active_sigsafe_mem()) {
985 		fflush(PHPDBG_G(io)[PHPDBG_STDOUT].ptr);
986 	}
987 } /* }}} */
988 
989 /* copied from sapi/cli/php_cli.c cli_register_file_handles */
phpdbg_register_file_handles(void)990 void phpdbg_register_file_handles(void) /* {{{ */
991 {
992 	zval zin, zout, zerr;
993 	php_stream *s_in, *s_out, *s_err;
994 	php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
995 	zend_constant ic, oc, ec;
996 
997 	s_in  = php_stream_open_wrapper_ex("php://stdin",  "rb", 0, NULL, sc_in);
998 	s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
999 	s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
1000 
1001 	if (s_in==NULL || s_out==NULL || s_err==NULL) {
1002 		if (s_in) php_stream_close(s_in);
1003 		if (s_out) php_stream_close(s_out);
1004 		if (s_err) php_stream_close(s_err);
1005 		return;
1006 	}
1007 
1008 #if PHP_DEBUG
1009 	/* do not close stdout and stderr */
1010 	s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
1011 	s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
1012 #endif
1013 
1014 	php_stream_to_zval(s_in,  &zin);
1015 	php_stream_to_zval(s_out, &zout);
1016 	php_stream_to_zval(s_err, &zerr);
1017 
1018 	ic.value = zin;
1019 	ZEND_CONSTANT_SET_FLAGS(&ic, CONST_CS, 0);
1020 	ic.name = zend_string_init(ZEND_STRL("STDIN"), 0);
1021 	zend_hash_del(EG(zend_constants), ic.name);
1022 	zend_register_constant(&ic);
1023 
1024 	oc.value = zout;
1025 	ZEND_CONSTANT_SET_FLAGS(&oc, CONST_CS, 0);
1026 	oc.name = zend_string_init(ZEND_STRL("STDOUT"), 0);
1027 	zend_hash_del(EG(zend_constants), oc.name);
1028 	zend_register_constant(&oc);
1029 
1030 	ec.value = zerr;
1031 	ZEND_CONSTANT_SET_FLAGS(&ec, CONST_CS, 0);
1032 	ec.name = zend_string_init(ZEND_STRL("STDERR"), 0);
1033 	zend_hash_del(EG(zend_constants), ec.name);
1034 	zend_register_constant(&ec);
1035 }
1036 /* }}} */
1037 
1038 /* {{{ sapi_module_struct phpdbg_sapi_module
1039 */
1040 static sapi_module_struct phpdbg_sapi_module = {
1041 	"phpdbg",                       /* name */
1042 	"phpdbg",                       /* pretty name */
1043 
1044 	php_sapi_phpdbg_module_startup, /* startup */
1045 	php_module_shutdown_wrapper,    /* shutdown */
1046 
1047 	php_sapi_phpdbg_activate,       /* activate */
1048 	php_sapi_phpdbg_deactivate,     /* deactivate */
1049 
1050 	php_sapi_phpdbg_ub_write,       /* unbuffered write */
1051 	php_sapi_phpdbg_flush,          /* flush */
1052 	NULL,                           /* get uid */
1053 	NULL,                           /* getenv */
1054 
1055 	php_error,                      /* error handler */
1056 
1057 	php_sapi_phpdbg_header_handler, /* header handler */
1058 	php_sapi_phpdbg_send_headers,   /* send headers handler */
1059 	php_sapi_phpdbg_send_header,    /* send header handler */
1060 
1061 	NULL,                           /* read POST data */
1062 	php_sapi_phpdbg_read_cookies,   /* read Cookies */
1063 
1064 	php_sapi_phpdbg_register_vars,  /* register server variables */
1065 	php_sapi_phpdbg_log_message,    /* Log message */
1066 	NULL,                           /* Get request time */
1067 	NULL,                           /* Child terminate */
1068 	STANDARD_SAPI_MODULE_PROPERTIES
1069 };
1070 /* }}} */
1071 
1072 const opt_struct OPTIONS[] = { /* {{{ */
1073 	{'c', 1, "ini path override"},
1074 	{'d', 1, "define ini entry on command line"},
1075 	{'n', 0, "no php.ini"},
1076 	{'z', 1, "load zend_extension"},
1077 	/* phpdbg options */
1078 	{'q', 0, "no banner"},
1079 	{'v', 0, "disable quietness"},
1080 	{'b', 0, "boring colours"},
1081 	{'i', 1, "specify init"},
1082 	{'I', 0, "ignore init"},
1083 	{'O', 1, "opline log"},
1084 	{'r', 0, "run"},
1085 	{'e', 0, "generate ext_stmt opcodes"},
1086 	{'E', 0, "step-through-eval"},
1087 	{'s', 1, "script from stdin"},
1088 	{'S', 1, "sapi-name"},
1089 #ifndef _WIN32
1090 	{'l', 1, "listen"},
1091 	{'a', 1, "address-or-any"},
1092 #endif
1093 	{'x', 0, "xml output"},
1094 	{'p', 2, "show opcodes"},
1095 	{'h', 0, "help"},
1096 	{'V', 0, "version"},
1097 	{'-', 0, NULL}
1098 }; /* }}} */
1099 
1100 const char phpdbg_ini_hardcoded[] =
1101 "html_errors=Off\n"
1102 "register_argc_argv=On\n"
1103 "implicit_flush=On\n"
1104 "display_errors=Off\n"
1105 "log_errors=On\n"
1106 "max_execution_time=0\n"
1107 "max_input_time=-1\n"
1108 "error_log=\n"
1109 "output_buffering=off\n\0";
1110 
1111 /* overwriteable ini defaults must be set in phpdbg_ini_defaults() */
1112 #define INI_DEFAULT(name, value) \
1113 	ZVAL_NEW_STR(&tmp, zend_string_init(value, sizeof(value) - 1, 1)); \
1114 	zend_hash_str_update(configuration_hash, name, sizeof(name) - 1, &tmp);
1115 
phpdbg_ini_defaults(HashTable * configuration_hash)1116 void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */
1117 {
1118 	zval tmp;
1119 	INI_DEFAULT("report_zend_debug", "0");
1120 } /* }}} */
1121 
phpdbg_welcome(zend_bool cleaning)1122 static void phpdbg_welcome(zend_bool cleaning) /* {{{ */
1123 {
1124 	/* print blurb */
1125 	if (!cleaning) {
1126 		phpdbg_xml("<intros>");
1127 		phpdbg_notice("intro", "version=\"%s\"", "Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
1128 		phpdbg_writeln("intro", "help=\"help\"", "To get help using phpdbg type \"help\" and press enter");
1129 		phpdbg_notice("intro", "report=\"%s\"", "Please report bugs to <%s>", PHPDBG_ISSUES);
1130 		phpdbg_xml("</intros>");
1131 	} else if (phpdbg_startup_run == 0) {
1132 		if (!(PHPDBG_G(flags) & PHPDBG_WRITE_XML)) {
1133 			phpdbg_notice(NULL, NULL, "Clean Execution Environment");
1134 		}
1135 
1136 		phpdbg_write("cleaninfo", "classes=\"%d\" functions=\"%d\" constants=\"%d\" includes=\"%d\"",
1137 			"Classes              %d\n"
1138 			"Functions            %d\n"
1139 			"Constants            %d\n"
1140 			"Includes             %d\n",
1141 			zend_hash_num_elements(EG(class_table)),
1142 			zend_hash_num_elements(EG(function_table)),
1143 			zend_hash_num_elements(EG(zend_constants)),
1144 			zend_hash_num_elements(&EG(included_files)));
1145 	}
1146 } /* }}} */
1147 
phpdbg_sigint_handler(int signo)1148 static inline void phpdbg_sigint_handler(int signo) /* {{{ */
1149 {
1150 
1151 	if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) {
1152 		/* we quit remote consoles on recv SIGINT */
1153 		if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
1154 			PHPDBG_G(flags) |= PHPDBG_IS_STOPPING;
1155 			zend_bailout();
1156 		}
1157 	} else {
1158 		/* set signalled only when not interactive */
1159 		if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
1160 			char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
1161 
1162 			phpdbg_set_sigsafe_mem(mem);
1163 			zend_try {
1164 				phpdbg_force_interruption();
1165 			} zend_end_try()
1166 			phpdbg_clear_sigsafe_mem();
1167 
1168 			PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED;
1169 
1170 			if (PHPDBG_G(flags) & PHPDBG_IS_STOPPING) {
1171 				zend_bailout();
1172 			}
1173 		} else {
1174 			PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
1175 			if (PHPDBG_G(flags) & PHPDBG_PREVENT_INTERACTIVE) {
1176 				PHPDBG_G(flags) |= PHPDBG_HAS_PAGINATION;
1177 				PHPDBG_G(flags) &= ~PHPDBG_PREVENT_INTERACTIVE;
1178 			}
1179 		}
1180 	}
1181 } /* }}} */
1182 
phpdbg_remote_close(int socket,FILE * stream)1183 static void phpdbg_remote_close(int socket, FILE *stream) {
1184 	if (socket >= 0) {
1185 		phpdbg_close_socket(socket);
1186 	}
1187 
1188 	if (stream) {
1189 		fclose(stream);
1190 	}
1191 }
1192 
1193 /* don't inline this, want to debug it easily, will inline when done */
phpdbg_remote_init(const char * address,unsigned short port,int server,int * socket,FILE ** stream)1194 static int phpdbg_remote_init(const char* address, unsigned short port, int server, int *socket, FILE **stream) {
1195 	phpdbg_remote_close(*socket, *stream);
1196 
1197 	if (server < 0) {
1198 		phpdbg_rlog(fileno(stderr), "Initializing connection on %s:%u failed", address, port);
1199 
1200 		return FAILURE;
1201 	}
1202 
1203 	phpdbg_rlog(fileno(stderr), "accepting connections on %s:%u", address, port);
1204 	{
1205 		struct sockaddr_storage address;
1206 		socklen_t size = sizeof(address);
1207 		char buffer[20] = {0};
1208 		/* XXX error checks */
1209 		memset(&address, 0, size);
1210 		*socket = accept(server, (struct sockaddr *) &address, &size);
1211 		inet_ntop(AF_INET, &(((struct sockaddr_in *)&address)->sin_addr), buffer, sizeof(buffer));
1212 
1213 		phpdbg_rlog(fileno(stderr), "connection established from %s", buffer);
1214 	}
1215 
1216 #ifndef _WIN32
1217 	dup2(*socket, fileno(stdout));
1218 	dup2(*socket, fileno(stdin));
1219 
1220 	setbuf(stdout, NULL);
1221 
1222 	*stream = fdopen(*socket, "r+");
1223 
1224 	phpdbg_set_async_io(*socket);
1225 #endif
1226 	return SUCCESS;
1227 }
1228 
1229 #ifndef _WIN32
1230 /* This function *strictly* assumes that SIGIO is *only* used on the remote connection stream */
phpdbg_sigio_handler(int sig,siginfo_t * info,void * context)1231 void phpdbg_sigio_handler(int sig, siginfo_t *info, void *context) /* {{{ */
1232 {
1233 	int flags;
1234 	size_t newlen;
1235 	size_t i/*, last_nl*/;
1236 
1237 //	if (!(info->si_band & POLLIN)) {
1238 //		return; /* Not interested in writeablility etc., just interested in incoming data */
1239 //	}
1240 
1241 	/* only non-blocking reading, avoid non-blocking writing */
1242 	flags = fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_GETFL, 0);
1243 	fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags | O_NONBLOCK);
1244 
1245 	do {
1246 		char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
1247 		size_t off = 0;
1248 
1249 		if ((newlen = recv(PHPDBG_G(io)[PHPDBG_STDIN].fd, mem, PHPDBG_SIGSAFE_MEM_SIZE, MSG_PEEK)) == (size_t) -1) {
1250 			break;
1251 		}
1252 		for (i = 0; i < newlen; i++) {
1253 			switch (mem[off + i]) {
1254 				case '\x03': /* ^C char */
1255 					if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) {
1256 						break; /* or quit ??? */
1257 					}
1258 					if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
1259 						phpdbg_set_sigsafe_mem(mem);
1260 						zend_try {
1261 							phpdbg_force_interruption();
1262 						} zend_end_try();
1263 						phpdbg_clear_sigsafe_mem();
1264 
1265 						PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED;
1266 
1267 						if (PHPDBG_G(flags) & PHPDBG_IS_STOPPING) {
1268 							zend_bailout();
1269 						}
1270 					} else if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
1271 						PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
1272 					}
1273 					break;
1274 /*				case '\n':
1275 					zend_llist_add_element(PHPDBG_G(stdin), strndup()
1276 					last_nl = PHPDBG_G(stdin_buf).len + i;
1277 					break;
1278 */			}
1279 		}
1280 		off += i;
1281 	} while (0);
1282 
1283 
1284 	fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags);
1285 } /* }}} */
1286 
phpdbg_signal_handler(int sig,siginfo_t * info,void * context)1287 void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
1288 {
1289 	int is_handled = FAILURE;
1290 
1291 	switch (sig) {
1292 		case SIGBUS:
1293 		case SIGSEGV:
1294 			is_handled = phpdbg_watchpoint_segfault_handler(info, context);
1295 			if (is_handled == FAILURE) {
1296 				if (PHPDBG_G(sigsegv_bailout)) {
1297 					LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
1298 				}
1299 				zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
1300 			}
1301 			break;
1302 	}
1303 
1304 } /* }}} */
1305 #endif
1306 
phpdbg_sighup_handler(int sig)1307 void phpdbg_sighup_handler(int sig) /* {{{ */
1308 {
1309 	exit(0);
1310 } /* }}} */
1311 
phpdbg_malloc_wrapper(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1312 void *phpdbg_malloc_wrapper(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1313 {
1314 	return _zend_mm_alloc(zend_mm_get_heap(), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1315 } /* }}} */
1316 
phpdbg_free_wrapper(void * p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1317 void phpdbg_free_wrapper(void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1318 {
1319 	zend_mm_heap *heap = zend_mm_get_heap();
1320 	if (UNEXPECTED(heap == p)) {
1321 		/* TODO: heap maybe allocated by mmap(zend_mm_init) or malloc(USE_ZEND_ALLOC=0)
1322 		 * let's prevent it from segfault for now
1323 		 */
1324 	} else {
1325 		phpdbg_watch_efree(p);
1326 		_zend_mm_free(heap, p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1327 	}
1328 } /* }}} */
1329 
phpdbg_realloc_wrapper(void * ptr,size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)1330 void *phpdbg_realloc_wrapper(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) /* {{{ */
1331 {
1332 	return _zend_mm_realloc(zend_mm_get_heap(), ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
1333 } /* }}} */
1334 
phpdbg_stream_url_wrap_php(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)1335 php_stream *phpdbg_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */
1336 {
1337 	if (!strncasecmp(path, "php://", 6)) {
1338 		path += 6;
1339 	}
1340 
1341 	if (!strncasecmp(path, "stdin", 6) && PHPDBG_G(stdin_file)) {
1342 		php_stream *stream = php_stream_fopen_from_fd(dup(fileno(PHPDBG_G(stdin_file))), "r", NULL);
1343 #ifdef PHP_WIN32
1344 		if (context != NULL) {
1345 			zval *blocking_pipes = php_stream_context_get_option(context, "pipe", "blocking");
1346 			if (blocking_pipes) {
1347 				convert_to_long(blocking_pipes);
1348 				php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, Z_LVAL_P(blocking_pipes), NULL);
1349 			}
1350 		}
1351 #endif
1352 		return stream;
1353 	}
1354 
1355 	return PHPDBG_G(orig_url_wrap_php)->wops->stream_opener(wrapper, path, mode, options, opened_path, context STREAMS_CC);
1356 } /* }}} */
1357 
main(int argc,char ** argv)1358 int main(int argc, char **argv) /* {{{ */
1359 {
1360 	sapi_module_struct *phpdbg = &phpdbg_sapi_module;
1361 	char *sapi_name;
1362 	char *ini_entries;
1363 	int   ini_entries_len;
1364 	char **zend_extensions = NULL;
1365 	zend_ulong zend_extensions_len = 0L;
1366 	zend_bool ini_ignore;
1367 	char *ini_override;
1368 	char *exec = NULL;
1369 	char *first_command = NULL;
1370 	char *init_file;
1371 	size_t init_file_len;
1372 	zend_bool init_file_default;
1373 	char *oplog_file;
1374 	size_t oplog_file_len;
1375 	uint64_t flags;
1376 	char *php_optarg;
1377 	int php_optind, opt, show_banner = 1;
1378 	long cleaning = -1;
1379 	volatile zend_bool quit_immediately = 0; /* somehow some gcc release builds will play a bit around with order in combination with setjmp..., hence volatile */
1380 	zend_bool remote = 0;
1381 	zend_phpdbg_globals *settings = NULL;
1382 	char *bp_tmp = NULL;
1383 	char *address;
1384 	int listen = -1;
1385 	int server = -1;
1386 	int socket = -1;
1387 	FILE* stream = NULL;
1388 	char *print_opline_func;
1389 	zend_bool ext_stmt = 0;
1390 	zend_bool is_exit;
1391 	int exit_status;
1392 	char *read_from_stdin = NULL;
1393 	zend_string *backup_phpdbg_compile = NULL;
1394 	zend_bool show_help = 0, show_version = 0;
1395 	void* (*_malloc)(size_t);
1396 	void (*_free)(void*);
1397 	void* (*_realloc)(void*, size_t);
1398 	php_stream_wrapper wrapper;
1399 	php_stream_wrapper_ops wops;
1400 
1401 
1402 #ifndef _WIN32
1403 	struct sigaction sigio_struct;
1404 	struct sigaction signal_struct;
1405 	signal_struct.sa_sigaction = phpdbg_signal_handler;
1406 	signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
1407 	sigemptyset(&signal_struct.sa_mask);
1408 	sigio_struct.sa_sigaction = phpdbg_sigio_handler;
1409 	sigio_struct.sa_flags = SA_SIGINFO;
1410 	sigemptyset(&sigio_struct.sa_mask);
1411 
1412 	address = strdup("127.0.0.1");
1413 #endif
1414 
1415 #ifdef PHP_WIN32
1416 	_fmode = _O_BINARY;                 /* sets default for file streams to binary */
1417 	setmode(_fileno(stdin), O_BINARY);  /* make the stdio mode be binary */
1418 	setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
1419 	setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
1420 #endif
1421 
1422 phpdbg_main:
1423 #ifdef ZTS
1424 	php_tsrm_startup();
1425 # ifdef PHP_WIN32
1426 	ZEND_TSRMLS_CACHE_UPDATE();
1427 # endif
1428 #endif
1429 
1430 	zend_signal_startup();
1431 
1432 	ini_entries = NULL;
1433 	ini_entries_len = 0;
1434 	ini_ignore = 0;
1435 	ini_override = NULL;
1436 	zend_extensions = NULL;
1437 	zend_extensions_len = 0L;
1438 	init_file = NULL;
1439 	init_file_len = 0;
1440 	init_file_default = 1;
1441 	oplog_file = NULL;
1442 	oplog_file_len = 0;
1443 	flags = PHPDBG_DEFAULT_FLAGS;
1444 	is_exit = 0;
1445 	php_optarg = NULL;
1446 	php_optind = 1;
1447 	opt = 0;
1448 	sapi_name = NULL;
1449 	exit_status = 0;
1450 	if (settings) {
1451 		exec = settings->exec;
1452 	}
1453 
1454 	while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
1455 		switch (opt) {
1456 			case 'r':
1457 				if (settings == NULL) {
1458 					phpdbg_startup_run++;
1459 				}
1460 				break;
1461 			case 'n':
1462 				ini_ignore = 1;
1463 				break;
1464 			case 'c':
1465 				if (ini_override) {
1466 					free(ini_override);
1467 				}
1468 				ini_override = strdup(php_optarg);
1469 				break;
1470 			case 'd': {
1471 				int len = strlen(php_optarg);
1472 				char *val;
1473 
1474 				if ((val = strchr(php_optarg, '='))) {
1475 				  val++;
1476 				  if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
1477 					  ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
1478 					  memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
1479 					  ini_entries_len += (val - php_optarg);
1480 					  memcpy(ini_entries + ini_entries_len, "\"", 1);
1481 					  ini_entries_len++;
1482 					  memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg));
1483 					  ini_entries_len += len - (val - php_optarg);
1484 					  memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
1485 					  ini_entries_len += sizeof("\n\0\"") - 2;
1486 				  } else {
1487 					  ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0"));
1488 					  memcpy(ini_entries + ini_entries_len, php_optarg, len);
1489 					  memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
1490 					  ini_entries_len += len + sizeof("\n\0") - 2;
1491 				  }
1492 				} else {
1493 				  ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
1494 				  memcpy(ini_entries + ini_entries_len, php_optarg, len);
1495 				  memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
1496 				  ini_entries_len += len + sizeof("=1\n\0") - 2;
1497 				}
1498 			} break;
1499 
1500 			case 'z':
1501 				zend_extensions_len++;
1502 				if (zend_extensions) {
1503 					zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len);
1504 				} else zend_extensions = malloc(sizeof(char*) * zend_extensions_len);
1505 				zend_extensions[zend_extensions_len-1] = strdup(php_optarg);
1506 			break;
1507 
1508 			/* begin phpdbg options */
1509 
1510 			case 's': { /* read script from stdin */
1511 				if (settings == NULL) {
1512 					read_from_stdin = strdup(php_optarg);
1513 				}
1514 			} break;
1515 
1516 			case 'S': { /* set SAPI name */
1517 				sapi_name = strdup(php_optarg);
1518 			} break;
1519 
1520 			case 'I': { /* ignore .phpdbginit */
1521 				init_file_default = 0;
1522 			} break;
1523 
1524 			case 'i': { /* set init file */
1525 				if (init_file) {
1526 					free(init_file);
1527 					init_file = NULL;
1528 				}
1529 
1530 				init_file_len = strlen(php_optarg);
1531 				if (init_file_len) {
1532 					init_file = strdup(php_optarg);
1533 				}
1534 			} break;
1535 
1536 			case 'O': { /* set oplog output */
1537 				oplog_file_len = strlen(php_optarg);
1538 				if (oplog_file_len) {
1539 					oplog_file = strdup(php_optarg);
1540 				}
1541 			} break;
1542 
1543 			case 'v': /* set quietness off */
1544 				flags &= ~PHPDBG_IS_QUIET;
1545 			break;
1546 
1547 			case 'e':
1548 				ext_stmt = 1;
1549 			break;
1550 
1551 			case 'E': /* stepping through eval on */
1552 				flags |= PHPDBG_IS_STEPONEVAL;
1553 			break;
1554 
1555 			case 'b': /* set colours off */
1556 				flags &= ~PHPDBG_IS_COLOURED;
1557 			break;
1558 
1559 			case 'q': /* hide banner */
1560 				show_banner = 0;
1561 			break;
1562 
1563 #ifndef _WIN32
1564 			/* if you pass a listen port, we will read and write on listen port */
1565 			case 'l': /* set listen ports */
1566 				if (sscanf(php_optarg, "%d", &listen) != 1) {
1567 					listen = 8000;
1568 				}
1569 			break;
1570 
1571 			case 'a': { /* set bind address */
1572 				free(address);
1573 				if (!php_optarg) {
1574 					address = strdup("*");
1575 				} else address = strdup(php_optarg);
1576 			} break;
1577 #endif
1578 
1579 			case 'x':
1580 				flags |= PHPDBG_WRITE_XML;
1581 			break;
1582 
1583 
1584 			case 'p': {
1585 				print_opline_func = php_optarg;
1586 				show_banner = 0;
1587 				settings = (void *) 0x1;
1588 			} break;
1589 
1590 			case 'h': {
1591 				show_help = 1;
1592 			} break;
1593 
1594 			case 'V': {
1595 				show_version = 1;
1596 			} break;
1597 		}
1598 
1599 		php_optarg = NULL;
1600 	}
1601 
1602 	quit_immediately = phpdbg_startup_run > 1;
1603 
1604 	/* set exec if present on command line */
1605 	if (!read_from_stdin && argc > php_optind) {
1606 		if (!exec && strlen(argv[php_optind])) {
1607 			exec = strdup(argv[php_optind]);
1608 		}
1609 		php_optind++;
1610 	}
1611 
1612 	if (sapi_name) {
1613 		phpdbg->name = sapi_name;
1614 	}
1615 
1616 	phpdbg->ini_defaults = phpdbg_ini_defaults;
1617 	phpdbg->phpinfo_as_text = 1;
1618 	phpdbg->php_ini_ignore_cwd = 1;
1619 
1620 	sapi_startup(phpdbg);
1621 
1622 	phpdbg->executable_location = argv[0];
1623 	phpdbg->phpinfo_as_text = 1;
1624 	phpdbg->php_ini_ignore = ini_ignore;
1625 	phpdbg->php_ini_path_override = ini_override;
1626 
1627 	if (ini_entries) {
1628 		ini_entries = realloc(ini_entries, ini_entries_len + sizeof(phpdbg_ini_hardcoded));
1629 		memmove(ini_entries + sizeof(phpdbg_ini_hardcoded) - 2, ini_entries, ini_entries_len + 1);
1630 		memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded) - 2);
1631 	} else {
1632 		ini_entries = malloc(sizeof(phpdbg_ini_hardcoded));
1633 		memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded));
1634 	}
1635 	ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2;
1636 
1637 	if (zend_extensions_len) {
1638 		zend_ulong zend_extension = 0L;
1639 
1640 		while (zend_extension < zend_extensions_len) {
1641 			const char *ze = zend_extensions[zend_extension];
1642 			size_t ze_len = strlen(ze);
1643 
1644 			ini_entries = realloc(
1645 				ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n"))));
1646 			memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1));
1647 			ini_entries_len += (sizeof("zend_extension=")-1);
1648 			memcpy(&ini_entries[ini_entries_len], ze, ze_len);
1649 			ini_entries_len += ze_len;
1650 			memcpy(&ini_entries[ini_entries_len], "\n", (sizeof("\n") - 1));
1651 
1652 			free(zend_extensions[zend_extension]);
1653 			zend_extension++;
1654 		}
1655 
1656 		free(zend_extensions);
1657 	}
1658 
1659 	phpdbg->ini_entries = ini_entries;
1660 
1661 	ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL);
1662 
1663 	/* set default colors */
1664 	phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT,  PHPDBG_STRL("white-bold"));
1665 	phpdbg_set_color_ex(PHPDBG_COLOR_ERROR,   PHPDBG_STRL("red-bold"));
1666 	phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE,  PHPDBG_STRL("green"));
1667 
1668 	if (settings > (zend_phpdbg_globals *) 0x2) {
1669 #ifdef ZTS
1670 		zend_phpdbg_globals *ptr = TSRMG_BULK_STATIC(phpdbg_globals_id, zend_phpdbg_globals *);
1671 		*ptr = *settings;
1672 #else
1673 		phpdbg_globals = *settings;
1674 #endif
1675 		free(settings);
1676 	} else {
1677 		/* set default prompt */
1678 		phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT);
1679 	}
1680 
1681 	/* set flags from command line */
1682 	PHPDBG_G(flags) = flags;
1683 
1684 	if (phpdbg->startup(phpdbg) == SUCCESS) {
1685 		zend_mm_heap *mm_heap;
1686 #ifdef _WIN32
1687     EXCEPTION_POINTERS *xp;
1688     __try {
1689 #endif
1690 
1691 		if (show_version || show_help) {
1692 			/* It ain't gonna proceed to real execution anyway,
1693 				but the correct descriptor is needed already. */
1694 			PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1695 			PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1696 			if (show_help) {
1697 				phpdbg_do_help_cmd(exec);
1698 			} else if (show_version) {
1699 				phpdbg_out(
1700 					"phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) The PHP Group\n%s",
1701 					PHPDBG_VERSION,
1702 					__DATE__,
1703 					__TIME__,
1704 					PHP_VERSION,
1705 					get_zend_version()
1706 				);
1707 			}
1708 			sapi_deactivate();
1709 			sapi_shutdown();
1710 			if (ini_entries) {
1711 				free(ini_entries);
1712 			}
1713 			if (ini_override) {
1714 				free(ini_override);
1715 			}
1716 			if (exec) {
1717 				free(exec);
1718 			}
1719 			if (oplog_file) {
1720 				free(oplog_file);
1721 			}
1722 			if (init_file) {
1723 				free(init_file);
1724 			}
1725 			goto free_and_return;
1726 		}
1727 
1728 		zend_try {
1729 			zend_signal_activate();
1730 		} zend_end_try();
1731 
1732 		/* setup remote server if necessary */
1733 		if (cleaning <= 0 && listen > 0) {
1734 			server = phpdbg_open_socket(address, listen);
1735 			if (-1 > server || phpdbg_remote_init(address, listen, server, &socket, &stream) == FAILURE) {
1736 				exit(0);
1737 			}
1738 
1739 #ifndef _WIN32
1740 			zend_sigaction(SIGIO, &sigio_struct, NULL);
1741 #endif
1742 
1743 			/* set remote flag to stop service shutting down upon quit */
1744 			remote = 1;
1745 #ifndef _WIN32
1746 		} else {
1747 
1748 			zend_signal(SIGHUP, phpdbg_sighup_handler);
1749 #endif
1750 		}
1751 
1752 		mm_heap = zend_mm_get_heap();
1753 		zend_mm_get_custom_handlers(mm_heap, &_malloc, &_free, &_realloc);
1754 
1755 		use_mm_wrappers = !_malloc && !_realloc && !_free;
1756 
1757 		PHPDBG_G(original_free_function) = _free;
1758 		_free = phpdbg_watch_efree;
1759 
1760 		if (use_mm_wrappers) {
1761 #if ZEND_DEBUG
1762 			zend_mm_set_custom_debug_handlers(mm_heap, phpdbg_malloc_wrapper, phpdbg_free_wrapper, phpdbg_realloc_wrapper);
1763 #else
1764 			zend_mm_set_custom_handlers(mm_heap, phpdbg_malloc_wrapper, phpdbg_free_wrapper, phpdbg_realloc_wrapper);
1765 #endif
1766 		} else {
1767 			zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc);
1768 		}
1769 
1770 		_free = PHPDBG_G(original_free_function);
1771 
1772 
1773 		phpdbg_init_list();
1774 
1775 		PHPDBG_G(sapi_name_ptr) = sapi_name;
1776 
1777 		if (exec) { /* set execution context */
1778 			PHPDBG_G(exec) = phpdbg_resolve_path(exec);
1779 			PHPDBG_G(exec_len) = PHPDBG_G(exec) ? strlen(PHPDBG_G(exec)) : 0;
1780 
1781 			free(exec);
1782 			exec = NULL;
1783 		}
1784 
1785 		php_output_activate();
1786 		php_output_deactivate();
1787 
1788 		if (SG(sapi_headers).mimetype) {
1789 			efree(SG(sapi_headers).mimetype);
1790 			SG(sapi_headers).mimetype = NULL;
1791 		}
1792 
1793 		php_output_activate();
1794 
1795 		{
1796 			int i;
1797 
1798 			SG(request_info).argc = argc - php_optind + 1;
1799 			SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *));
1800 			for (i = SG(request_info).argc; --i;) {
1801 				SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]);
1802 			}
1803 			SG(request_info).argv[0] = PHPDBG_G(exec) ? estrdup(PHPDBG_G(exec)) : estrdup("");
1804 		}
1805 
1806 		if (php_request_startup() == FAILURE) {
1807 			PUTS("Could not startup");
1808 #ifndef _WIN32
1809 			if (address) {
1810 				free(address);
1811 			}
1812 #endif
1813 			return 1;
1814 		}
1815 
1816 #ifndef _WIN32
1817 		zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1818 		zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1819 #endif
1820 
1821 		/* do not install sigint handlers for remote consoles */
1822 		/* sending SIGINT then provides a decent way of shutting down the server */
1823 #ifndef _WIN32
1824 		if (listen < 0) {
1825 #endif
1826 			zend_try { zend_signal(SIGINT, phpdbg_sigint_handler); } zend_end_try();
1827 #ifndef _WIN32
1828 		}
1829 
1830 		/* setup io here */
1831 		if (remote) {
1832 			PHPDBG_G(flags) |= PHPDBG_IS_REMOTE;
1833 			zend_signal(SIGPIPE, SIG_IGN);
1834 		}
1835 		PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
1836 		PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
1837 		PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1838 		PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1839 #else
1840 		/* XXX this is a complete mess here with FILE/fd/SOCKET,
1841 			we should let only one to survive probably. Need
1842 			a clean separation whether it's a remote or local
1843 			prompt. And what is supposed to go as user interaction,
1844 			error log, etc. */
1845 		if (remote) {
1846 			PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
1847 			PHPDBG_G(io)[PHPDBG_STDIN].fd = socket;
1848 			PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1849 			PHPDBG_G(io)[PHPDBG_STDOUT].fd = socket;
1850 		} else {
1851 			PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
1852 			PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
1853 			PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1854 			PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1855 		}
1856 #endif
1857 		PHPDBG_G(io)[PHPDBG_STDERR].ptr = stderr;
1858 		PHPDBG_G(io)[PHPDBG_STDERR].fd = fileno(stderr);
1859 
1860 #ifndef _WIN32
1861 		PHPDBG_G(php_stdiop_write) = php_stream_stdio_ops.write;
1862 		php_stream_stdio_ops.write = phpdbg_stdiop_write;
1863 #endif
1864 
1865 		if (oplog_file) { /* open oplog */
1866 			PHPDBG_G(oplog) = fopen(oplog_file, "w+");
1867 			if (!PHPDBG_G(oplog)) {
1868 				phpdbg_error("oplog", "path=\"%s\"", "Failed to open oplog %s", oplog_file);
1869 			}
1870 			free(oplog_file);
1871 			oplog_file = NULL;
1872 		}
1873 
1874 		{
1875 			zval *zv = zend_hash_str_find(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
1876 			php_stream_wrapper *tmp_wrapper = Z_PTR_P(zv);
1877 			PHPDBG_G(orig_url_wrap_php) = tmp_wrapper;
1878 			memcpy(&wrapper, tmp_wrapper, sizeof(wrapper));
1879 			memcpy(&wops, tmp_wrapper->wops, sizeof(wops));
1880 			wops.stream_opener = phpdbg_stream_url_wrap_php;
1881 			wrapper.wops = (const php_stream_wrapper_ops*)&wops;
1882 			Z_PTR_P(zv) = &wrapper;
1883 		}
1884 
1885 		/* Make stdin, stdout and stderr accessible from PHP scripts */
1886 		phpdbg_register_file_handles();
1887 
1888 		phpdbg_list_update();
1889 
1890 		if (show_banner && cleaning < 2) {
1891 			/* print blurb */
1892 			phpdbg_welcome(cleaning == 1);
1893 		}
1894 
1895 		cleaning = -1;
1896 
1897 		if (ext_stmt) {
1898 			CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
1899 		}
1900 
1901 		/* initialize from file */
1902 		PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
1903 		zend_try {
1904 			phpdbg_init(init_file, init_file_len, init_file_default);
1905 		} zend_end_try();
1906 		PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
1907 
1908 		/* quit if init says so */
1909 		if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
1910 			goto phpdbg_out;
1911 		}
1912 
1913 		/* auto compile */
1914 		if (read_from_stdin) {
1915 			if (!read_from_stdin[0]) {
1916 				if (!quit_immediately) {
1917 					phpdbg_error("error", "", "Impossible to not specify a stdin delimiter without -rr");
1918 					PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
1919 					goto phpdbg_out;
1920 				}
1921 			}
1922 			if (show_banner || read_from_stdin[0]) {
1923 				phpdbg_notice("stdin", "delimiter=\"%s\"", "Reading input from stdin; put '%s' followed by a newline on an own line after code to end input", read_from_stdin);
1924 			}
1925 
1926 			if (phpdbg_startup_run > 0) {
1927 				PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1928 			}
1929 
1930 			zend_try {
1931 				phpdbg_param_t cmd;
1932 				cmd.str = read_from_stdin;
1933 				cmd.len = strlen(read_from_stdin);
1934 				PHPDBG_COMMAND_HANDLER(stdin)(&cmd);
1935 			} zend_end_try();
1936 
1937 			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1938 		} else if (PHPDBG_G(exec)) {
1939 			if (settings || phpdbg_startup_run > 0) {
1940 				PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1941 			}
1942 
1943 			zend_try {
1944 				if (backup_phpdbg_compile) {
1945 					phpdbg_compile_stdin(backup_phpdbg_compile);
1946 				} else {
1947 					phpdbg_compile();
1948 				}
1949 			} zend_end_try();
1950 			backup_phpdbg_compile = NULL;
1951 
1952 			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1953 		}
1954 
1955 		if (bp_tmp) {
1956 			PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT | PHPDBG_IS_INITIALIZING;
1957 			phpdbg_string_init(bp_tmp);
1958 			free(bp_tmp);
1959 			bp_tmp = NULL;
1960 			PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT & ~PHPDBG_IS_INITIALIZING;
1961 		}
1962 
1963 		if (settings == (void *) 0x1) {
1964 			if (PHPDBG_G(ops)) {
1965 				phpdbg_print_opcodes(print_opline_func);
1966 			} else {
1967 				zend_quiet_write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("No opcodes could be compiled | No file specified or compilation failed?\n"));
1968 			}
1969 			goto phpdbg_out;
1970 		}
1971 
1972 		PG(during_request_startup) = 0;
1973 
1974 		phpdbg_fully_started = 1;
1975 
1976 /* #ifndef for making compiler shutting up */
1977 #ifndef _WIN32
1978 phpdbg_interact:
1979 #endif
1980 		/* phpdbg main() */
1981 		do {
1982 			zend_try {
1983 				if (phpdbg_startup_run) {
1984 					phpdbg_startup_run = 0;
1985 					if (quit_immediately) {
1986 						PHPDBG_G(flags) = (PHPDBG_G(flags) & ~PHPDBG_HAS_PAGINATION) | PHPDBG_IS_INTERACTIVE | PHPDBG_PREVENT_INTERACTIVE;
1987 					} else {
1988 						PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
1989 					}
1990 					zend_try {
1991 						if (first_command) {
1992 							phpdbg_interactive(1, estrdup(first_command));
1993 						} else {
1994 							PHPDBG_COMMAND_HANDLER(run)(NULL);
1995 						}
1996 					} zend_end_try();
1997 					if (quit_immediately) {
1998 						/* if -r is on the command line more than once just quit */
1999 						EG(bailout) = __orig_bailout; /* reset zend_try */
2000 						exit_status = EG(exit_status);
2001 						break;
2002 					}
2003 				}
2004 
2005 				CG(unclean_shutdown) = 0;
2006 				phpdbg_interactive(1, NULL);
2007 			} zend_catch {
2008 				if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
2009 					char *bp_tmp_str;
2010 					PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
2011 					phpdbg_export_breakpoints_to_string(&bp_tmp_str);
2012 					PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
2013 					if (bp_tmp_str) {
2014 						bp_tmp = strdup(bp_tmp_str);
2015 						efree(bp_tmp_str);
2016 					}
2017 					cleaning = 1;
2018 				} else {
2019 					cleaning = 0;
2020 				}
2021 
2022 #ifndef _WIN32
2023 				if (!cleaning) {
2024 					/* remote client disconnected */
2025 					if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
2026 
2027 						if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
2028 							/* renegociate connections */
2029 							phpdbg_remote_init(address, listen, server, &socket, &stream);
2030 
2031 							/* set streams */
2032 							if (stream) {
2033 								PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING;
2034 							}
2035 
2036 							/* this must be forced */
2037 							CG(unclean_shutdown) = 0;
2038 						} else {
2039 							/* local consoles cannot disconnect, ignore EOF */
2040 							PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
2041 						}
2042 					}
2043 				}
2044 #endif
2045 			} zend_end_try();
2046 		} while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
2047 
2048 
2049 #ifndef _WIN32
2050 phpdbg_out:
2051 		if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
2052 			PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
2053 			goto phpdbg_interact;
2054 		}
2055 #endif
2056 
2057 #ifdef _WIN32
2058 	} __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
2059 		phpdbg_error("segfault", "", "Access violation (Segmentation fault) encountered\ntrying to abort cleanly...");
2060 	}
2061 phpdbg_out:
2062 #endif
2063 
2064 		phpdbg_purge_watchpoint_tree();
2065 
2066 		if (first_command) {
2067 			free(first_command);
2068 			first_command = NULL;
2069 		}
2070 
2071 		if (cleaning <= 0) {
2072 			PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING;
2073 			cleaning = -1;
2074 		}
2075 
2076 		{
2077 			int i;
2078 			/* free argv */
2079 			for (i = SG(request_info).argc; i--;) {
2080 				efree(SG(request_info).argv[i]);
2081 			}
2082 			efree(SG(request_info).argv);
2083 		}
2084 
2085 		if (ini_entries) {
2086 			free(ini_entries);
2087 		}
2088 
2089 		if (ini_override) {
2090 			free(ini_override);
2091 		}
2092 
2093 		/* In case we aborted during script execution, we may not reset CG(unclean_shutdown) */
2094 		if (!(PHPDBG_G(flags) & PHPDBG_IS_RUNNING)) {
2095 			is_exit = !PHPDBG_G(in_execution);
2096 			CG(unclean_shutdown) = is_exit || PHPDBG_G(unclean_eval);
2097 		}
2098 
2099 		if ((PHPDBG_G(flags) & (PHPDBG_IS_CLEANING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_CLEANING) {
2100 			php_free_shutdown_functions();
2101 			zend_objects_store_mark_destructed(&EG(objects_store));
2102 		}
2103 
2104 		if (PHPDBG_G(exec) && strcmp("Standard input code", PHPDBG_G(exec)) == SUCCESS) { /* i.e. execution context has been read from stdin - back it up */
2105 			phpdbg_file_source *data = zend_hash_str_find_ptr(&PHPDBG_G(file_sources), PHPDBG_G(exec), PHPDBG_G(exec_len));
2106 			backup_phpdbg_compile = zend_string_alloc(data->len + 2, 1);
2107 			GC_MAKE_PERSISTENT_LOCAL(backup_phpdbg_compile);
2108 			sprintf(ZSTR_VAL(backup_phpdbg_compile), "?>%.*s", (int) data->len, data->buf);
2109 		}
2110 
2111 		zend_try {
2112 			php_request_shutdown(NULL);
2113 		} zend_end_try();
2114 
2115 		/* backup globals when cleaning */
2116 		if ((cleaning > 0 || remote) && !quit_immediately) {
2117 			settings = calloc(1, sizeof(zend_phpdbg_globals));
2118 
2119 			php_phpdbg_globals_ctor(settings);
2120 
2121 			if (PHPDBG_G(exec)) {
2122 				settings->exec = zend_strndup(PHPDBG_G(exec), PHPDBG_G(exec_len));
2123 				settings->exec_len = PHPDBG_G(exec_len);
2124 			}
2125 			settings->oplog = PHPDBG_G(oplog);
2126 			settings->prompt[0] = PHPDBG_G(prompt)[0];
2127 			settings->prompt[1] = PHPDBG_G(prompt)[1];
2128 			memcpy(settings->colors, PHPDBG_G(colors), sizeof(settings->colors));
2129 			settings->eol = PHPDBG_G(eol);
2130 			settings->input_buflen = PHPDBG_G(input_buflen);
2131 			memcpy(settings->input_buffer, PHPDBG_G(input_buffer), settings->input_buflen);
2132 			settings->flags = PHPDBG_G(flags) & PHPDBG_PRESERVE_FLAGS_MASK;
2133 			first_command = PHPDBG_G(cur_command);
2134 		} else {
2135 			if (PHPDBG_G(prompt)[0]) {
2136 				free(PHPDBG_G(prompt)[0]);
2137 			}
2138 			if (PHPDBG_G(prompt)[1]) {
2139 				free(PHPDBG_G(prompt)[1]);
2140 			}
2141 			if (PHPDBG_G(cur_command)) {
2142 				free(PHPDBG_G(cur_command));
2143 			}
2144 		}
2145 
2146 		if (exit_status == 0) {
2147 			exit_status = EG(exit_status);
2148 		}
2149 
2150 		php_output_deactivate();
2151 
2152 		if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
2153 			PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
2154 			if (PHPDBG_G(in_execution) || is_exit) {
2155 				if (!quit_immediately && !phpdbg_startup_run) {
2156 					PHPDBG_G(flags) -= PHPDBG_IS_QUITTING;
2157 					cleaning++;
2158 				}
2159 			}
2160 		}
2161 
2162 		{
2163 			zval *zv = zend_hash_str_find(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
2164 			Z_PTR_P(zv) = (void*)PHPDBG_G(orig_url_wrap_php);
2165 		}
2166 
2167 #ifndef _WIN32
2168 		/* force override (no zend_signals) to prevent crashes due to signal recursion in SIGSEGV/SIGBUS handlers */
2169 		signal(SIGSEGV, SIG_DFL);
2170 		signal(SIGBUS, SIG_DFL);
2171 
2172 		/* reset it... else we risk a stack overflow upon next run (when clean'ing) */
2173 		php_stream_stdio_ops.write = PHPDBG_G(php_stdiop_write);
2174 #endif
2175 	}
2176 
2177 	php_module_shutdown();
2178 
2179 	sapi_shutdown();
2180 
2181 	if (sapi_name) {
2182 		free(sapi_name);
2183 	}
2184 
2185 free_and_return:
2186 	if (read_from_stdin) {
2187 		free(read_from_stdin);
2188 		read_from_stdin = NULL;
2189 	}
2190 
2191 #ifdef ZTS
2192 	/* reset to original handlers - otherwise PHPDBG_G() in phpdbg_watch_efree will be segfaulty (with e.g. USE_ZEND_ALLOC=0) */
2193 	if (!use_mm_wrappers) {
2194 		zend_mm_set_custom_handlers(zend_mm_get_heap(), _malloc, _free, _realloc);
2195 	}
2196 
2197 	ts_free_id(phpdbg_globals_id);
2198 
2199 	tsrm_shutdown();
2200 #endif
2201 
2202 	if ((cleaning > 0 || remote) && !quit_immediately) {
2203 		/* reset internal php_getopt state */
2204 		php_getopt(-1, argv, OPTIONS, NULL, &php_optind, 0, 0);
2205 
2206 		goto phpdbg_main;
2207 	}
2208 
2209 	if (backup_phpdbg_compile) {
2210 		zend_string_free(backup_phpdbg_compile);
2211 	}
2212 
2213 #ifndef _WIN32
2214 	if (address) {
2215 		free(address);
2216 	}
2217 #endif
2218 
2219 	/* usually 0; just for -rr */
2220 	return exit_status;
2221 } /* }}} */
2222