xref: /PHP-5.6/sapi/phpdbg/phpdbg.c (revision b1f33db3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2016 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 #if !defined(ZEND_SIGNALS) || defined(_WIN32)
22 # include <signal.h>
23 #endif
24 #include "phpdbg.h"
25 #include "phpdbg_prompt.h"
26 #include "phpdbg_bp.h"
27 #include "phpdbg_break.h"
28 #include "phpdbg_list.h"
29 #include "phpdbg_utils.h"
30 #include "phpdbg_set.h"
31 #include "zend_alloc.h"
32 
33 /* {{{ remote console headers */
34 #ifndef _WIN32
35 #	include <sys/socket.h>
36 #	include <sys/select.h>
37 #	include <sys/time.h>
38 #	include <sys/types.h>
39 #	include <netinet/in.h>
40 #	include <unistd.h>
41 #	include <arpa/inet.h>
42 #endif /* }}} */
43 
44 #if defined(PHP_WIN32) && defined(HAVE_OPENSSL)
45 # include "openssl/applink.c"
46 #endif
47 
48 ZEND_DECLARE_MODULE_GLOBALS(phpdbg);
49 
50 static zend_bool phpdbg_booted = 0;
51 
52 #if PHP_VERSION_ID >= 50500
53 void (*zend_execute_old)(zend_execute_data *execute_data TSRMLS_DC);
54 #else
55 void (*zend_execute_old)(zend_op_array *op_array TSRMLS_DC);
56 #endif
57 
php_phpdbg_globals_ctor(zend_phpdbg_globals * pg)58 static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
59 {
60 	pg->prompt[0] = NULL;
61 	pg->prompt[1] = NULL;
62 
63 	pg->colors[0] = NULL;
64 	pg->colors[1] = NULL;
65 	pg->colors[2] = NULL;
66 
67 	pg->exec = NULL;
68 	pg->exec_len = 0;
69 	pg->buffer = NULL;
70 	pg->ops = NULL;
71 	pg->vmret = 0;
72 	pg->bp_count = 0;
73 	pg->flags = PHPDBG_DEFAULT_FLAGS;
74 	pg->oplog = NULL;
75 	pg->io[PHPDBG_STDIN] = NULL;
76 	pg->io[PHPDBG_STDOUT] = NULL;
77 	pg->io[PHPDBG_STDERR] = NULL;
78 	pg->frame.num = 0;
79 } /* }}} */
80 
PHP_MINIT_FUNCTION(phpdbg)81 static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
82 {
83 	ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL);
84 #if PHP_VERSION_ID >= 50500
85 	zend_execute_old = zend_execute_ex;
86 	zend_execute_ex = phpdbg_execute_ex;
87 #else
88 	zend_execute_old = zend_execute;
89 	zend_execute = phpdbg_execute_ex;
90 #endif
91 
92 	REGISTER_STRINGL_CONSTANT("PHPDBG_VERSION", PHPDBG_VERSION, sizeof(PHPDBG_VERSION)-1, CONST_CS|CONST_PERSISTENT);
93 
94 	REGISTER_LONG_CONSTANT("PHPDBG_FILE",   FILE_PARAM, CONST_CS|CONST_PERSISTENT);
95 	REGISTER_LONG_CONSTANT("PHPDBG_METHOD", METHOD_PARAM, CONST_CS|CONST_PERSISTENT);
96 	REGISTER_LONG_CONSTANT("PHPDBG_LINENO", NUMERIC_PARAM, CONST_CS|CONST_PERSISTENT);
97 	REGISTER_LONG_CONSTANT("PHPDBG_FUNC",   STR_PARAM, CONST_CS|CONST_PERSISTENT);
98 
99 	REGISTER_LONG_CONSTANT("PHPDBG_COLOR_PROMPT", PHPDBG_COLOR_PROMPT, CONST_CS|CONST_PERSISTENT);
100 	REGISTER_LONG_CONSTANT("PHPDBG_COLOR_NOTICE", PHPDBG_COLOR_NOTICE, CONST_CS|CONST_PERSISTENT);
101 	REGISTER_LONG_CONSTANT("PHPDBG_COLOR_ERROR",  PHPDBG_COLOR_ERROR, CONST_CS|CONST_PERSISTENT);
102 
103 	return SUCCESS;
104 } /* }}} */
105 
php_phpdbg_destroy_bp_file(void * brake)106 static void php_phpdbg_destroy_bp_file(void *brake) /* {{{ */
107 {
108 	zend_hash_destroy((HashTable*)brake);
109 } /* }}} */
110 
php_phpdbg_destroy_bp_symbol(void * brake)111 static void php_phpdbg_destroy_bp_symbol(void *brake) /* {{{ */
112 {
113 	efree((char*)((phpdbg_breaksymbol_t*)brake)->symbol);
114 } /* }}} */
115 
php_phpdbg_destroy_bp_opcode(void * brake)116 static void php_phpdbg_destroy_bp_opcode(void *brake) /* {{{ */
117 {
118 	efree((char*)((phpdbg_breakop_t*)brake)->name);
119 } /* }}} */
120 
121 
php_phpdbg_destroy_bp_methods(void * brake)122 static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */
123 {
124 	zend_hash_destroy((HashTable*)brake);
125 } /* }}} */
126 
php_phpdbg_destroy_bp_condition(void * data)127 static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */
128 {
129 	phpdbg_breakcond_t *brake = (phpdbg_breakcond_t*) data;
130 
131 	if (brake) {
132 		if (brake->ops) {
133 			TSRMLS_FETCH();
134 
135 			destroy_op_array(
136 					brake->ops TSRMLS_CC);
137 			efree(brake->ops);
138 		}
139 		efree((char*)brake->code);
140 	}
141 } /* }}} */
142 
php_phpdbg_destroy_registered(void * data)143 static void php_phpdbg_destroy_registered(void *data) /* {{{ */
144 {
145 	zend_function *function = (zend_function*) data;
146 	TSRMLS_FETCH();
147 
148 	destroy_zend_function(
149 		function TSRMLS_CC);
150 } /* }}} */
151 
152 
PHP_RINIT_FUNCTION(phpdbg)153 static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
154 {
155 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE],   8, NULL, php_phpdbg_destroy_bp_file, 0);
156 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
157 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
158 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
159 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
160 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 0);
161 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0);
162 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
163 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
164 	zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
165 
166 	zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
167 	zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
168 
169 	return SUCCESS;
170 } /* }}} */
171 
PHP_RSHUTDOWN_FUNCTION(phpdbg)172 static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
173 {
174 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
175 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
176 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
177 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
178 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
179 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
180 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
181 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
182 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
183 	zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
184 	zend_hash_destroy(&PHPDBG_G(seek));
185 	zend_hash_destroy(&PHPDBG_G(registered));
186 	zend_hash_destroy(&PHPDBG_G(watchpoints));
187 	zend_llist_destroy(&PHPDBG_G(watchlist_mem));
188 
189 	if (PHPDBG_G(buffer)) {
190 		efree(PHPDBG_G(buffer));
191 		PHPDBG_G(buffer) = NULL;
192 	}
193 
194 	if (PHPDBG_G(exec)) {
195 		efree(PHPDBG_G(exec));
196 		PHPDBG_G(exec) = NULL;
197 	}
198 
199 	if (PHPDBG_G(prompt)[0]) {
200 		free(PHPDBG_G(prompt)[0]);
201 	}
202 	if (PHPDBG_G(prompt)[1]) {
203 		free(PHPDBG_G(prompt)[1]);
204 	}
205 
206 	PHPDBG_G(prompt)[0] = NULL;
207 	PHPDBG_G(prompt)[1] = NULL;
208 
209 	if (PHPDBG_G(oplog)) {
210 		fclose(
211 				PHPDBG_G(oplog));
212 		PHPDBG_G(oplog) = NULL;
213 	}
214 
215 	if (PHPDBG_G(ops)) {
216 		destroy_op_array(PHPDBG_G(ops) TSRMLS_CC);
217 		efree(PHPDBG_G(ops));
218 		PHPDBG_G(ops) = NULL;
219 	}
220 
221 	return SUCCESS;
222 } /* }}} */
223 
224 /* {{{ proto mixed phpdbg_exec(string context)
225 	Attempt to set the execution context for phpdbg
226 	If the execution context was set previously it is returned
227 	If the execution context was not set previously boolean true is returned
228 	If the request to set the context fails, boolean false is returned, and an E_WARNING raised */
PHP_FUNCTION(phpdbg_exec)229 static PHP_FUNCTION(phpdbg_exec)
230 {
231 	char *exec = NULL;
232 	int exec_len = 0;
233 
234 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) {
235 		return;
236 	}
237 
238 	{
239 		struct stat sb;
240 		zend_bool result = 1;
241 
242 		if (VCWD_STAT(exec, &sb) != FAILURE) {
243 			if (sb.st_mode & (S_IFREG|S_IFLNK)) {
244 				if (PHPDBG_G(exec)) {
245 					ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len), 1);
246 					efree(PHPDBG_G(exec));
247 					result = 0;
248 				}
249 
250 				PHPDBG_G(exec) = estrndup(exec, exec_len);
251 				PHPDBG_G(exec_len) = exec_len;
252 
253 				if (result)
254 					ZVAL_BOOL(return_value, 1);
255 			} else {
256 				zend_error(
257 					E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", exec);
258 				ZVAL_BOOL(return_value, 0);
259 			}
260 		} else {
261 			zend_error(
262 				E_WARNING, "Failed to set execution context (%s) the file does not exist", exec);
263 
264 			ZVAL_BOOL(return_value, 0);
265 		}
266 	}
267 } /* }}} */
268 
269 /* {{{ proto void phpdbg_break_next()
270     instructs phpdbg to insert a breakpoint at the next opcode */
PHP_FUNCTION(phpdbg_break_next)271 static PHP_FUNCTION(phpdbg_break_next)
272 {
273 	if (zend_parse_parameters_none() != SUCCESS) {
274 		return;
275 	} else if (EG(current_execute_data) && EG(active_op_array)) {
276 		zend_ulong opline_num = (EG(current_execute_data)->opline -
277 				EG(active_op_array)->opcodes);
278 
279 		phpdbg_set_breakpoint_opline_ex(
280 				&EG(active_op_array)->opcodes[opline_num+1] TSRMLS_CC);
281 	}
282 } /* }}} */
283 
284 /* {{{ proto void phpdbg_break_file(string file, integer line) */
PHP_FUNCTION(phpdbg_break_file)285 static PHP_FUNCTION(phpdbg_break_file)
286 {
287     char    *file = NULL;
288     int      flen = 0;
289     long     line;
290 
291     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &file, &flen, &line) == FAILURE) {
292         return;
293     }
294 
295     phpdbg_set_breakpoint_file(file, line TSRMLS_CC);
296 } /* }}} */
297 
298 /* {{{ proto void phpdbg_break_method(string class, string method) */
PHP_FUNCTION(phpdbg_break_method)299 static PHP_FUNCTION(phpdbg_break_method)
300 {
301     char *class = NULL,
302          *method = NULL;
303     int clen = 0,
304         mlen = 0;
305 
306     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &class, &clen, &method, &mlen) == FAILURE) {
307     return;
308     }
309 
310     phpdbg_set_breakpoint_method(class, method TSRMLS_CC);
311 } /* }}} */
312 
313 /* {{{ proto void phpdbg_break_function(string function) */
PHP_FUNCTION(phpdbg_break_function)314 static PHP_FUNCTION(phpdbg_break_function)
315 {
316     char *function = NULL;
317     int   function_len;
318 
319     if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &function, &function_len) == FAILURE) {
320            return;
321     }
322 
323     phpdbg_set_breakpoint_symbol(function, function_len TSRMLS_CC);
324 } /* }}} */
325 
326 /* {{{ proto void phpdbg_clear(void)
327    instructs phpdbg to clear breakpoints */
PHP_FUNCTION(phpdbg_clear)328 static PHP_FUNCTION(phpdbg_clear)
329 {
330 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
331 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
332 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
333 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
334 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
335 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
336 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
337 	zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
338 } /* }}} */
339 
340 /* {{{ proto void phpdbg_color(integer element, string color) */
PHP_FUNCTION(phpdbg_color)341 static PHP_FUNCTION(phpdbg_color)
342 {
343 	long element = 0L;
344 	char *color = NULL;
345 	int color_len = 0;
346 
347 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) {
348 		return;
349 	}
350 
351 	switch (element) {
352 		case PHPDBG_COLOR_NOTICE:
353 		case PHPDBG_COLOR_ERROR:
354 		case PHPDBG_COLOR_PROMPT:
355 			phpdbg_set_color_ex(element, color, color_len TSRMLS_CC);
356 		break;
357 
358 		default: zend_error(E_ERROR, "phpdbg detected an incorrect color constant");
359 	}
360 } /* }}} */
361 
362 /* {{{ proto void phpdbg_prompt(string prompt) */
PHP_FUNCTION(phpdbg_prompt)363 static PHP_FUNCTION(phpdbg_prompt)
364 {
365 	char *prompt = NULL;
366 	int prompt_len = 0;
367 
368 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) {
369 		return;
370 	}
371 
372 	phpdbg_set_prompt(prompt TSRMLS_CC);
373 } /* }}} */
374 
375 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_next_arginfo, 0, 0, 0)
376 ZEND_END_ARG_INFO()
377 
378 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_file_arginfo, 0, 0, 2)
379     ZEND_ARG_INFO(0, file)
380     ZEND_ARG_INFO(0, line)
381 ZEND_END_ARG_INFO()
382 
383 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_method_arginfo, 0, 0, 2)
384     ZEND_ARG_INFO(0, class)
385     ZEND_ARG_INFO(0, method)
386 ZEND_END_ARG_INFO()
387 
388 ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_function_arginfo, 0, 0, 1)
389     ZEND_ARG_INFO(0, function)
390 ZEND_END_ARG_INFO()
391 
392 ZEND_BEGIN_ARG_INFO_EX(phpdbg_color_arginfo, 0, 0, 0)
393 	ZEND_ARG_INFO(0, element)
394 	ZEND_ARG_INFO(0, color)
395 ZEND_END_ARG_INFO()
396 
397 ZEND_BEGIN_ARG_INFO_EX(phpdbg_prompt_arginfo, 0, 0, 0)
398 	ZEND_ARG_INFO(0, string)
399 ZEND_END_ARG_INFO()
400 
401 ZEND_BEGIN_ARG_INFO_EX(phpdbg_exec_arginfo, 0, 0, 0)
402 	ZEND_ARG_INFO(0, context)
403 ZEND_END_ARG_INFO()
404 
405 ZEND_BEGIN_ARG_INFO_EX(phpdbg_clear_arginfo, 0, 0, 0)
406 ZEND_END_ARG_INFO()
407 
408 zend_function_entry phpdbg_user_functions[] = {
409 	PHP_FE(phpdbg_clear, phpdbg_clear_arginfo)
410 	PHP_FE(phpdbg_break_next, phpdbg_break_next_arginfo)
411 	PHP_FE(phpdbg_break_file, phpdbg_break_file_arginfo)
412 	PHP_FE(phpdbg_break_method, phpdbg_break_method_arginfo)
413 	PHP_FE(phpdbg_break_function, phpdbg_break_function_arginfo)
414 	PHP_FE(phpdbg_exec,  phpdbg_exec_arginfo)
415 	PHP_FE(phpdbg_color, phpdbg_color_arginfo)
416 	PHP_FE(phpdbg_prompt, phpdbg_prompt_arginfo)
417 #ifdef  PHP_FE_END
418 	PHP_FE_END
419 #else
420 	{NULL,NULL,NULL}
421 #endif
422 };
423 
424 static zend_module_entry sapi_phpdbg_module_entry = {
425 	STANDARD_MODULE_HEADER,
426 	PHPDBG_NAME,
427 	phpdbg_user_functions,
428 	PHP_MINIT(phpdbg),
429 	NULL,
430 	PHP_RINIT(phpdbg),
431 	PHP_RSHUTDOWN(phpdbg),
432 	NULL,
433 	PHPDBG_VERSION,
434 	STANDARD_MODULE_PROPERTIES
435 };
436 
php_sapi_phpdbg_module_startup(sapi_module_struct * module)437 static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */
438 {
439 	if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) {
440 		return FAILURE;
441 	}
442 
443 	phpdbg_booted=1;
444 
445 	return SUCCESS;
446 } /* }}} */
447 
php_sapi_phpdbg_read_cookies(TSRMLS_D)448 static char* php_sapi_phpdbg_read_cookies(TSRMLS_D) /* {{{ */
449 {
450 	return NULL;
451 } /* }}} */
452 
php_sapi_phpdbg_header_handler(sapi_header_struct * h,sapi_header_op_enum op,sapi_headers_struct * s TSRMLS_DC)453 static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s TSRMLS_DC) /* {{{ */
454 {
455 	return 0;
456 }
457 /* }}} */
458 
php_sapi_phpdbg_send_headers(sapi_headers_struct * sapi_headers TSRMLS_DC)459 static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */
460 {
461 	/* We do nothing here, this function is needed to prevent that the fallback
462 	 * header handling is called. */
463 	return SAPI_HEADER_SENT_SUCCESSFULLY;
464 }
465 /* }}} */
466 
php_sapi_phpdbg_send_header(sapi_header_struct * sapi_header,void * server_context TSRMLS_DC)467 static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC) /* {{{ */
468 {
469 }
470 /* }}} */
471 
php_sapi_phpdbg_log_message(char * message TSRMLS_DC)472 static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */
473 {
474 	/*
475 	* We must not request TSRM before being boot
476 	*/
477 	if (phpdbg_booted) {
478 		phpdbg_error("%s", message);
479 
480 		switch (PG(last_error_type)) {
481 			case E_ERROR:
482 			case E_CORE_ERROR:
483 			case E_COMPILE_ERROR:
484 			case E_USER_ERROR:
485 			case E_PARSE:
486 			case E_RECOVERABLE_ERROR:
487 				if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) {
488 					phpdbg_list_file(
489 						zend_get_executed_filename(TSRMLS_C),
490 						3,
491 						zend_get_executed_lineno(TSRMLS_C)-1,
492 						zend_get_executed_lineno(TSRMLS_C)
493 						TSRMLS_CC
494 					);
495 				}
496 
497 				do {
498 					switch (phpdbg_interactive(TSRMLS_C)) {
499 						case PHPDBG_LEAVE:
500 						case PHPDBG_FINISH:
501 						case PHPDBG_UNTIL:
502 						case PHPDBG_NEXT:
503 							return;
504 					}
505 				} while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
506 
507 		}
508 	} else fprintf(stdout, "%s\n", message);
509 }
510 /* }}} */
511 
php_sapi_phpdbg_deactivate(TSRMLS_D)512 static int php_sapi_phpdbg_deactivate(TSRMLS_D) /* {{{ */
513 {
514 	fflush(stdout);
515 	if(SG(request_info).argv0) {
516 		free(SG(request_info).argv0);
517 		SG(request_info).argv0 = NULL;
518 	}
519 	return SUCCESS;
520 }
521 /* }}} */
522 
php_sapi_phpdbg_register_vars(zval * track_vars_array TSRMLS_DC)523 static void php_sapi_phpdbg_register_vars(zval *track_vars_array TSRMLS_DC) /* {{{ */
524 {
525 	unsigned int len;
526 	char   *docroot = "";
527 
528 	/* In phpdbg mode, we consider the environment to be a part of the server variables
529 	*/
530 	php_import_environment_variables(track_vars_array TSRMLS_CC);
531 
532 	if (PHPDBG_G(exec)) {
533 		len = PHPDBG_G(exec_len);
534 		if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF",
535 					&PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
536 			php_register_variable("PHP_SELF", PHPDBG_G(exec),
537 					track_vars_array TSRMLS_CC);
538 		}
539 		if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME",
540 					&PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
541 			php_register_variable("SCRIPT_NAME", PHPDBG_G(exec),
542 					track_vars_array TSRMLS_CC);
543 		}
544 
545 		if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME",
546 					&PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
547 			php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec),
548 					track_vars_array TSRMLS_CC);
549 		}
550 		if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED",
551 					&PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) {
552 			php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec),
553 					track_vars_array TSRMLS_CC);
554 		}
555 	}
556 
557 	/* any old docroot will doo */
558 	len = 0U;
559 	if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT",
560 				&docroot, len, &len TSRMLS_CC)) {
561 		php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array TSRMLS_CC);
562 	}
563 }
564 /* }}} */
565 
php_sapi_phpdbg_ub_write(const char * message,unsigned int length TSRMLS_DC)566 static inline int php_sapi_phpdbg_ub_write(const char *message, unsigned int length TSRMLS_DC) /* {{{ */
567 {
568 	return phpdbg_write("%s", message);
569 } /* }}} */
570 
571 #if PHP_VERSION_ID >= 50700
php_sapi_phpdbg_flush(void * context TSRMLS_DC)572 static inline void php_sapi_phpdbg_flush(void *context TSRMLS_DC)  /* {{{ */
573 {
574 #else
575 static inline void php_sapi_phpdbg_flush(void *context)  /* {{{ */
576 {
577 	TSRMLS_FETCH();
578 #endif
579 
580 	fflush(PHPDBG_G(io)[PHPDBG_STDOUT]);
581 } /* }}} */
582 
583 /* copied from sapi/cli/php_cli.c cli_register_file_handles */
584 static void phpdbg_register_file_handles(TSRMLS_D) /* {{{ */
585 {
586 	zval *zin, *zout, *zerr;
587 	php_stream *s_in, *s_out, *s_err;
588 	php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
589 	zend_constant ic, oc, ec;
590 
591 	MAKE_STD_ZVAL(zin);
592 	MAKE_STD_ZVAL(zout);
593 	MAKE_STD_ZVAL(zerr);
594 
595 	s_in  = php_stream_open_wrapper_ex("php://stdin",  "rb", 0, NULL, sc_in);
596 	s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
597 	s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
598 
599 	if (s_in==NULL || s_out==NULL || s_err==NULL) {
600 		FREE_ZVAL(zin);
601 		FREE_ZVAL(zout);
602 		FREE_ZVAL(zerr);
603 		if (s_in) php_stream_close(s_in);
604 		if (s_out) php_stream_close(s_out);
605 		if (s_err) php_stream_close(s_err);
606 		return;
607 	}
608 
609 #if PHP_DEBUG
610 	/* do not close stdout and stderr */
611 	s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
612 	s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
613 #endif
614 
615 	php_stream_to_zval(s_in,  zin);
616 	php_stream_to_zval(s_out, zout);
617 	php_stream_to_zval(s_err, zerr);
618 
619 	ic.value = *zin;
620 	ic.flags = CONST_CS;
621 	ic.name = zend_strndup(ZEND_STRL("STDIN"));
622 	ic.name_len = sizeof("STDIN");
623 	ic.module_number = 0;
624 	zend_register_constant(&ic TSRMLS_CC);
625 
626 	oc.value = *zout;
627 	oc.flags = CONST_CS;
628 	oc.name = zend_strndup(ZEND_STRL("STDOUT"));
629 	oc.name_len = sizeof("STDOUT");
630 	oc.module_number = 0;
631 	zend_register_constant(&oc TSRMLS_CC);
632 
633 	ec.value = *zerr;
634 	ec.flags = CONST_CS;
635 	ec.name = zend_strndup(ZEND_STRL("STDERR"));
636 	ec.name_len = sizeof("STDERR");
637 	ec.module_number = 0;
638 	zend_register_constant(&ec TSRMLS_CC);
639 
640 	FREE_ZVAL(zin);
641 	FREE_ZVAL(zout);
642 	FREE_ZVAL(zerr);
643 }
644 /* }}} */
645 
646 /* {{{ sapi_module_struct phpdbg_sapi_module
647 */
648 static sapi_module_struct phpdbg_sapi_module = {
649 	"phpdbg",                       /* name */
650 	"phpdbg",                       /* pretty name */
651 
652 	php_sapi_phpdbg_module_startup, /* startup */
653 	php_module_shutdown_wrapper,    /* shutdown */
654 
655 	NULL,                           /* activate */
656 	php_sapi_phpdbg_deactivate,     /* deactivate */
657 
658 	php_sapi_phpdbg_ub_write,       /* unbuffered write */
659 	php_sapi_phpdbg_flush,          /* flush */
660 	NULL,                           /* get uid */
661 	NULL,                           /* getenv */
662 
663 	php_error,                      /* error handler */
664 
665 	php_sapi_phpdbg_header_handler, /* header handler */
666 	php_sapi_phpdbg_send_headers,   /* send headers handler */
667 	php_sapi_phpdbg_send_header,    /* send header handler */
668 
669 	NULL,                           /* read POST data */
670 	php_sapi_phpdbg_read_cookies,   /* read Cookies */
671 
672 	php_sapi_phpdbg_register_vars,  /* register server variables */
673 	php_sapi_phpdbg_log_message,    /* Log message */
674 	NULL,                           /* Get request time */
675 	NULL,                           /* Child terminate */
676 	STANDARD_SAPI_MODULE_PROPERTIES
677 };
678 /* }}} */
679 
680 const opt_struct OPTIONS[] = { /* {{{ */
681 	{'c', 1, "ini path override"},
682 	{'d', 1, "define ini entry on command line"},
683 	{'n', 0, "no php.ini"},
684 	{'z', 1, "load zend_extension"},
685 	/* phpdbg options */
686 	{'q', 0, "no banner"},
687 	{'v', 0, "disable quietness"},
688 	{'s', 0, "enable stepping"},
689 	{'b', 0, "boring colours"},
690 	{'i', 1, "specify init"},
691 	{'I', 0, "ignore init"},
692 	{'O', 1, "opline log"},
693 	{'r', 0, "run"},
694 	{'E', 0, "step-through-eval"},
695 	{'S', 1, "sapi-name"},
696 #ifndef _WIN32
697 	{'l', 1, "listen"},
698 	{'a', 1, "address-or-any"},
699 #endif
700 	{'V', 0, "version"},
701 	{'-', 0, NULL}
702 }; /* }}} */
703 
704 const char phpdbg_ini_hardcoded[] =
705 "html_errors=Off\n"
706 "register_argc_argv=On\n"
707 "implicit_flush=On\n"
708 "display_errors=Off\n"
709 "log_errors=On\n"
710 "max_execution_time=0\n"
711 "max_input_time=-1\n"
712 "error_log=\n"
713 "output_buffering=off\0";
714 
715 /* overwriteable ini defaults must be set in phpdbg_ini_defaults() */
716 #define INI_DEFAULT(name, value) \
717 	Z_SET_REFCOUNT(tmp, 0); \
718 	Z_UNSET_ISREF(tmp); \
719 	ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \
720 	zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL);
721 
722 void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */
723 {
724 	zval tmp;
725 	INI_DEFAULT("report_zend_debug", "0");
726 } /* }}} */
727 
728 static void phpdbg_welcome(zend_bool cleaning TSRMLS_DC) /* {{{ */
729 {
730 	/* print blurb */
731 	if (!cleaning) {
732 		phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s",
733 				PHPDBG_VERSION);
734 		phpdbg_writeln("To get help using phpdbg type \"help\" and press enter");
735 		phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES);
736 	} else {
737 		phpdbg_notice("Clean Execution Environment");
738 
739 		phpdbg_writeln("Classes\t\t\t%d", zend_hash_num_elements(EG(class_table)));
740 		phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(EG(function_table)));
741 		phpdbg_writeln("Constants\t\t%d", zend_hash_num_elements(EG(zend_constants)));
742 		phpdbg_writeln("Includes\t\t%d",  zend_hash_num_elements(&EG(included_files)));
743 	}
744 } /* }}} */
745 
746 static inline void phpdbg_sigint_handler(int signo) /* {{{ */
747 {
748 	TSRMLS_FETCH();
749 
750 	if (EG(in_execution)) {
751 		/* set signalled only when not interactive */
752 		if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
753 			PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
754 		}
755 	} else {
756 		/* we quit remote consoles on recv SIGINT */
757 		if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
758 			PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
759 			zend_bailout();
760 		}
761 	}
762 } /* }}} */
763 
764 #ifndef _WIN32
765 int phpdbg_open_socket(const char *interface, short port) /* {{{ */
766 {
767 	int fd = socket(AF_INET, SOCK_STREAM, 0);
768 
769 	switch (fd) {
770 		case -1:
771 			return -1;
772 
773 		default: {
774 			int reuse = 1;
775 
776 			switch (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse))) {
777 				case -1:
778 					close(fd);
779 					return -2;
780 
781 				default: {
782 					struct sockaddr_in address;
783 
784 					memset(&address, 0, sizeof(address));
785 
786 					address.sin_port = htons(port);
787 					address.sin_family = AF_INET;
788 
789 					if ((*interface == '*')) {
790 						address.sin_addr.s_addr = htonl(INADDR_ANY);
791 					} else if (!inet_pton(AF_INET, interface, &address.sin_addr)) {
792 						close(fd);
793 						return -3;
794 					}
795 
796 					switch (bind(fd, (struct sockaddr *)&address, sizeof(address))) {
797 						case -1:
798 							close(fd);
799 							return -4;
800 
801 						default: {
802 							listen(fd, 5);
803 						}
804 					}
805 				}
806 			}
807 		}
808 	}
809 
810 	return fd;
811 } /* }}} */
812 
813 static inline void phpdbg_close_sockets(int (*socket)[2], FILE *streams[2]) /* {{{ */
814 {
815 	if ((*socket)[0] >= 0) {
816 		shutdown(
817 			(*socket)[0], SHUT_RDWR);
818 		close((*socket)[0]);
819 	}
820 
821 	if (streams[0]) {
822 		fclose(streams[0]);
823 	}
824 
825 	if ((*socket)[1] >= 0) {
826 		shutdown(
827 			(*socket)[1], SHUT_RDWR);
828 		close((*socket)[1]);
829 	}
830 
831 	if (streams[1]) {
832 		fclose(streams[1]);
833 	}
834 } /* }}} */
835 
836 /* don't inline this, want to debug it easily, will inline when done */
837 
838 int phpdbg_open_sockets(char *address, int port[2], int (*listen)[2], int (*socket)[2], FILE* streams[2]) /* {{{ */
839 {
840 	if (((*listen)[0]) < 0 && ((*listen)[1]) < 0) {
841 		((*listen)[0]) = phpdbg_open_socket(address, (short)port[0]);
842 		((*listen)[1]) = phpdbg_open_socket(address, (short)port[1]);
843 	}
844 
845 	streams[0] = NULL;
846 	streams[1] = NULL;
847 
848 	if ((*listen)[0] < 0 || (*listen)[1] < 0) {
849 		if ((*listen)[0] < 0) {
850 			phpdbg_rlog(stderr,
851 				"console failed to initialize (stdin) on %s:%d", address, port[0]);
852 		}
853 
854 		if ((*listen)[1] < 0) {
855 			phpdbg_rlog(stderr,
856 				"console failed to initialize (stdout) on %s:%d", address, port[1]);
857 		}
858 
859 		if ((*listen)[0] >= 0) {
860 			close((*listen)[0]);
861 		}
862 
863 		if ((*listen)[1] >= 0) {
864 			close((*listen)[1]);
865 		}
866 
867 		return FAILURE;
868 	}
869 
870 	phpdbg_close_sockets(socket, streams);
871 
872 	phpdbg_rlog(stderr,
873 		"accepting connections on %s:%d/%d", address, port[0], port[1]);
874 	{
875 		struct sockaddr_in address;
876 		socklen_t size = sizeof(address);
877 		char buffer[20] = {0};
878 
879 		{
880 			memset(&address, 0, size);
881 			(*socket)[0] = accept(
882 				(*listen)[0], (struct sockaddr *) &address, &size);
883 			inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer));
884 
885 			phpdbg_rlog(stderr, "connection (stdin) from %s", buffer);
886 		}
887 
888 		{
889 			memset(&address, 0, size);
890 			(*socket)[1] = accept(
891 				(*listen)[1], (struct sockaddr *) &address, &size);
892 		    inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer));
893 
894 			phpdbg_rlog(stderr, "connection (stdout) from %s", buffer);
895 		}
896 	}
897 
898 	dup2((*socket)[0], fileno(stdin));
899 	dup2((*socket)[1], fileno(stdout));
900 
901 	setbuf(stdout, NULL);
902 
903 	streams[0] = fdopen((*socket)[0], "r");
904 	streams[1] = fdopen((*socket)[1], "w");
905 
906 	return SUCCESS;
907 } /* }}} */
908 
909 void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
910 {
911 	int is_handled = FAILURE;
912 	TSRMLS_FETCH();
913 
914 	switch (sig) {
915 		case SIGBUS:
916 		case SIGSEGV:
917 			is_handled = phpdbg_watchpoint_segfault_handler(info, context TSRMLS_CC);
918 			if (is_handled == FAILURE) {
919 #ifdef ZEND_SIGNALS
920 				zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL TSRMLS_CC);
921 #else
922 				sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
923 #endif
924 			}
925 			break;
926 	}
927 
928 } /* }}} */
929 #endif
930 
931 static inline zend_mm_heap *phpdbg_mm_get_heap() /* {{{ */
932 {
933 	zend_mm_heap *mm_heap;
934 
935 	TSRMLS_FETCH();
936 
937 	mm_heap = zend_mm_set_heap(NULL TSRMLS_CC);
938 	zend_mm_set_heap(mm_heap TSRMLS_CC);
939 
940 	return mm_heap;
941 } /* }}} */
942 
943 void *phpdbg_malloc_wrapper(size_t size) /* {{{ */
944 {
945 	return zend_mm_alloc(phpdbg_mm_get_heap(), size);
946 } /* }}} */
947 
948 void phpdbg_free_wrapper(void *p) /* {{{ */
949 {
950 	zend_mm_free(phpdbg_mm_get_heap(), p);
951 } /* }}} */
952 
953 void *phpdbg_realloc_wrapper(void *ptr, size_t size) /* {{{ */
954 {
955 	return zend_mm_realloc(phpdbg_mm_get_heap(), ptr, size);
956 } /* }}} */
957 
958 int main(int argc, char **argv) /* {{{ */
959 {
960 	sapi_module_struct *phpdbg = &phpdbg_sapi_module;
961 	char *sapi_name;
962 	char *ini_entries;
963 	int   ini_entries_len;
964 	char **zend_extensions = NULL;
965 	zend_ulong zend_extensions_len = 0L;
966 	zend_bool ini_ignore;
967 	char *ini_override;
968 	char *exec;
969 	size_t exec_len;
970 	char *init_file;
971 	size_t init_file_len;
972 	zend_bool init_file_default;
973 	char *oplog_file;
974 	size_t oplog_file_len;
975 	zend_ulong flags;
976 	char *php_optarg;
977 	int php_optind, opt, show_banner = 1;
978 	long cleaning = 0;
979 	zend_bool remote = 0;
980 	int run = 0;
981 	int step = 0;
982 
983 #ifdef _WIN32
984 	char *bp_tmp_file = NULL;
985 #else
986 	char bp_tmp_file[] = "/tmp/phpdbg.XXXXXX";
987 #endif
988 
989 #ifndef _WIN32
990 	char *address;
991 	int listen[2];
992 	int server[2];
993 	int socket[2];
994 	FILE* streams[2] = {NULL, NULL};
995 #endif
996 
997 #ifdef ZTS
998 	void ***tsrm_ls;
999 #endif
1000 
1001 #ifndef _WIN32
1002 	struct sigaction signal_struct;
1003 	signal_struct.sa_sigaction = phpdbg_signal_handler;
1004 	signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
1005 
1006 	address = strdup("127.0.0.1");
1007 	socket[0] = -1;
1008 	socket[1] = -1;
1009 	listen[0] = -1;
1010 	listen[1] = -1;
1011 	server[0] = -1;
1012 	server[1] = -1;
1013 	streams[0] = NULL;
1014 	streams[1] = NULL;
1015 #endif
1016 
1017 #ifdef PHP_WIN32
1018 	_fmode = _O_BINARY;                 /* sets default for file streams to binary */
1019 	setmode(_fileno(stdin), O_BINARY);  /* make the stdio mode be binary */
1020 	setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
1021 	setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
1022 #endif
1023 
1024 #ifdef ZTS
1025 	tsrm_startup(1, 1, 0, NULL);
1026 
1027 	tsrm_ls = ts_resource(0);
1028 #endif
1029 
1030 phpdbg_main:
1031 	if (!cleaning) {
1032 
1033 #ifdef _WIN32
1034 		bp_tmp_file = malloc(L_tmpnam);
1035 
1036 		if (bp_tmp_file) {
1037 			if (!tmpnam(bp_tmp_file)) {
1038 				free(bp_tmp_file);
1039 				bp_tmp_file = NULL;
1040 			}
1041 		}
1042 
1043 		if (!bp_tmp_file) {
1044 			phpdbg_error("Unable to create temporary file");
1045 			return 1;
1046 		}
1047 #else
1048 		if (!mkstemp(bp_tmp_file)) {
1049 			memset(bp_tmp_file, 0, sizeof(bp_tmp_file));
1050 		}
1051 #endif
1052 
1053 	}
1054 	ini_entries = NULL;
1055 	ini_entries_len = 0;
1056 	ini_ignore = 0;
1057 	ini_override = NULL;
1058 	zend_extensions = NULL;
1059 	zend_extensions_len = 0L;
1060 	exec = NULL;
1061 	exec_len = 0;
1062 	init_file = NULL;
1063 	init_file_len = 0;
1064 	init_file_default = 1;
1065 	oplog_file = NULL;
1066 	oplog_file_len = 0;
1067 	flags = PHPDBG_DEFAULT_FLAGS;
1068 	php_optarg = NULL;
1069 	php_optind = 1;
1070 	opt = 0;
1071 	run = 0;
1072 	step = 0;
1073 	sapi_name = NULL;
1074 
1075 	while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
1076 		switch (opt) {
1077 			case 'r':
1078 				run++;
1079 				break;
1080 			case 'n':
1081 				ini_ignore = 1;
1082 				break;
1083 			case 'c':
1084 				if (ini_override) {
1085 					free(ini_override);
1086 				}
1087 				ini_override = strdup(php_optarg);
1088 				break;
1089 			case 'd': {
1090 				int len = strlen(php_optarg);
1091 				char *val;
1092 
1093 				if ((val = strchr(php_optarg, '='))) {
1094 				  val++;
1095 				  if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
1096 					  ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
1097 					  memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
1098 					  ini_entries_len += (val - php_optarg);
1099 					  memcpy(ini_entries + ini_entries_len, "\"", 1);
1100 					  ini_entries_len++;
1101 					  memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg));
1102 					  ini_entries_len += len - (val - php_optarg);
1103 					  memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
1104 					  ini_entries_len += sizeof("\n\0\"") - 2;
1105 				  } else {
1106 					  ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0"));
1107 					  memcpy(ini_entries + ini_entries_len, php_optarg, len);
1108 					  memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
1109 					  ini_entries_len += len + sizeof("\n\0") - 2;
1110 				  }
1111 				} else {
1112 				  ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
1113 				  memcpy(ini_entries + ini_entries_len, php_optarg, len);
1114 				  memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
1115 				  ini_entries_len += len + sizeof("=1\n\0") - 2;
1116 				}
1117 			} break;
1118 
1119 			case 'z':
1120 				zend_extensions_len++;
1121 				if (zend_extensions) {
1122 					zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len);
1123 				} else zend_extensions = malloc(sizeof(char*) * zend_extensions_len);
1124 				zend_extensions[zend_extensions_len-1] = strdup(php_optarg);
1125 			break;
1126 
1127 			/* begin phpdbg options */
1128 
1129 			case 'S': { /* set SAPI name */
1130 				if (sapi_name) {
1131 					free(sapi_name);
1132 				}
1133 				sapi_name = strdup(php_optarg);
1134 			} break;
1135 
1136 			case 'I': { /* ignore .phpdbginit */
1137 				init_file_default = 0;
1138 			} break;
1139 
1140 			case 'i': { /* set init file */
1141 				if (init_file) {
1142 					free(init_file);
1143 				}
1144 
1145 				init_file_len = strlen(php_optarg);
1146 				if (init_file_len) {
1147 					init_file = strdup(php_optarg);
1148 				}
1149 			} break;
1150 
1151 			case 'O': { /* set oplog output */
1152 				oplog_file_len = strlen(php_optarg);
1153 				if (oplog_file_len) {
1154 					oplog_file = strdup(php_optarg);
1155 				}
1156 			} break;
1157 
1158 			case 'v': /* set quietness off */
1159 				flags &= ~PHPDBG_IS_QUIET;
1160 			break;
1161 
1162 			case 's': /* set stepping on */
1163 				step = 1;
1164 			break;
1165 
1166 			case 'E': /* stepping through eval on */
1167 				flags |= PHPDBG_IS_STEPONEVAL;
1168 			break;
1169 
1170 			case 'b': /* set colours off */
1171 				flags &= ~PHPDBG_IS_COLOURED;
1172 			break;
1173 
1174 			case 'q': /* hide banner */
1175 				show_banner = 0;
1176 			break;
1177 
1178 #ifndef _WIN32
1179 			/* if you pass a listen port, we will accept input on listen port */
1180 			/* and write output to listen port * 2 */
1181 
1182 			case 'l': { /* set listen ports */
1183 				if (sscanf(php_optarg, "%d/%d", &listen[0], &listen[1]) != 2) {
1184 					if (sscanf(php_optarg, "%d", &listen[0]) != 1) {
1185 						/* default to hardcoded ports */
1186 						listen[0] = 4000;
1187 						listen[1] = 8000;
1188 					} else {
1189 						listen[1] = (listen[0] * 2);
1190 					}
1191 				}
1192 			} break;
1193 
1194 			case 'a': { /* set bind address */
1195 				free(address);
1196 				if (!php_optarg) {
1197 					address = strdup("*");
1198 				} else address = strdup(php_optarg);
1199 			} break;
1200 #endif
1201 
1202 			case 'V': {
1203 				sapi_startup(phpdbg);
1204 				phpdbg->startup(phpdbg);
1205 				printf(
1206 					"phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) 1997-2016 The PHP Group\n%s",
1207 					PHPDBG_VERSION,
1208 					__DATE__,
1209 					__TIME__,
1210 					PHP_VERSION,
1211 					get_zend_version()
1212 				);
1213 				sapi_deactivate(TSRMLS_C);
1214 				sapi_shutdown();
1215 				return 0;
1216 			} break;
1217 		}
1218 	}
1219 
1220 	/* set exec if present on command line */
1221 	if ((argc > php_optind) && (strcmp(argv[php_optind-1],"--") != SUCCESS))
1222 	{
1223 		exec_len = strlen(argv[php_optind]);
1224 		if (exec_len) {
1225 			if (exec) {
1226 				free(exec);
1227 			}
1228 			exec = strdup(argv[php_optind]);
1229 		}
1230 		php_optind++;
1231 	}
1232 
1233 #ifndef _WIN32
1234 	/* setup remote server if necessary */
1235 	if (!cleaning &&
1236 		(listen[0] > 0 && listen[1] > 0)) {
1237 		if (phpdbg_open_sockets(address, listen, &server, &socket, streams) == FAILURE) {
1238 			remote = 0;
1239 			exit(0);
1240 		}
1241 		/* set remote flag to stop service shutting down upon quit */
1242 		remote = 1;
1243 	}
1244 #endif
1245 
1246 	if (sapi_name) {
1247 		phpdbg->name = sapi_name;
1248 	}
1249 
1250 	phpdbg->ini_defaults = phpdbg_ini_defaults;
1251 	phpdbg->phpinfo_as_text = 1;
1252 	phpdbg->php_ini_ignore_cwd = 1;
1253 
1254 	sapi_startup(phpdbg);
1255 
1256 	phpdbg->executable_location = argv[0];
1257 	phpdbg->phpinfo_as_text = 1;
1258 	phpdbg->php_ini_ignore = ini_ignore;
1259 	phpdbg->php_ini_path_override = ini_override;
1260 
1261 	if (ini_entries) {
1262 		ini_entries = realloc(ini_entries, ini_entries_len + sizeof(phpdbg_ini_hardcoded));
1263 		memmove(ini_entries + sizeof(phpdbg_ini_hardcoded) - 2, ini_entries, ini_entries_len + 1);
1264 		memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded) - 2);
1265 	} else {
1266 		ini_entries = malloc(sizeof(phpdbg_ini_hardcoded));
1267 		memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded));
1268 	}
1269 	ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2;
1270 
1271 	if (zend_extensions_len) {
1272 		zend_ulong zend_extension = 0L;
1273 
1274 		while (zend_extension < zend_extensions_len) {
1275 			const char *ze = zend_extensions[zend_extension];
1276 			size_t ze_len = strlen(ze);
1277 
1278 			ini_entries = realloc(
1279 				ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n"))));
1280 			memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1));
1281 			ini_entries_len += (sizeof("zend_extension=")-1);
1282 			memcpy(&ini_entries[ini_entries_len], ze, ze_len);
1283 			ini_entries_len += ze_len;
1284 			memcpy(&ini_entries[ini_entries_len], "\n", (sizeof("\n") - 1));
1285 
1286 			free(zend_extensions[zend_extension]);
1287 			zend_extension++;
1288 		}
1289 
1290 		free(zend_extensions);
1291 	}
1292 
1293 	phpdbg->ini_entries = ini_entries;
1294 
1295 	if (phpdbg->startup(phpdbg) == SUCCESS) {
1296 #ifdef _WIN32
1297     EXCEPTION_POINTERS *xp;
1298     __try {
1299 #endif
1300 		zend_mm_heap *mm_heap = phpdbg_mm_get_heap();
1301 
1302 		if (mm_heap->use_zend_alloc) {
1303 			mm_heap->_malloc = phpdbg_malloc_wrapper;
1304 			mm_heap->_realloc = phpdbg_realloc_wrapper;
1305 			mm_heap->_free = phpdbg_free_wrapper;
1306 			mm_heap->use_zend_alloc = 0;
1307 		}
1308 
1309 		zend_activate(TSRMLS_C);
1310 
1311 		PHPDBG_G(original_free_function) = mm_heap->_free;
1312 		mm_heap->_free = phpdbg_watch_efree;
1313 
1314 		phpdbg_setup_watchpoints(TSRMLS_C);
1315 
1316 #if defined(ZEND_SIGNALS) && !defined(_WIN32)
1317 		zend_try {
1318 			zend_signal_activate(TSRMLS_C);
1319 		} zend_end_try();
1320 #endif
1321 
1322 #if defined(ZEND_SIGNALS) && !defined(_WIN32)
1323 		zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try();
1324 		zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try();
1325 #elif !defined(_WIN32)
1326 		sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
1327 		sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
1328 #endif
1329 
1330 		if (php_request_startup(TSRMLS_C) == SUCCESS) {
1331 			int i;
1332 
1333 			SG(request_info).argc = argc - php_optind + 1;
1334 			SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *));
1335 			for (i = SG(request_info).argc; --i;) {
1336 				SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]);
1337 			}
1338 			SG(request_info).argv[i] = exec ? estrndup(exec, exec_len) : estrdup("");
1339 
1340 			php_hash_environment(TSRMLS_C);
1341 		}
1342 
1343 		/* make sure to turn off buffer for ev command */
1344 		php_output_activate(TSRMLS_C);
1345 		php_output_deactivate(TSRMLS_C);
1346 
1347 		/* do not install sigint handlers for remote consoles */
1348 		/* sending SIGINT then provides a decent way of shutting down the server */
1349 #ifndef _WIN32
1350 		if (listen[0] < 0) {
1351 #endif
1352 #if defined(ZEND_SIGNALS) && !defined(_WIN32)
1353 			zend_try { zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); } zend_end_try();
1354 #else
1355 			signal(SIGINT, phpdbg_sigint_handler);
1356 #endif
1357 #ifndef _WIN32
1358 		}
1359 #endif
1360 
1361 		PG(modules_activated) = 0;
1362 
1363 		/* set flags from command line */
1364 		PHPDBG_G(flags) = flags;
1365 
1366 #ifndef _WIN32
1367 		/* setup io here */
1368 		if (streams[0] && streams[1]) {
1369 			PHPDBG_G(flags) |= PHPDBG_IS_REMOTE;
1370 
1371 			signal(SIGPIPE, SIG_IGN);
1372 		}
1373 #endif
1374 
1375 		PHPDBG_G(io)[PHPDBG_STDIN] = stdin;
1376 		PHPDBG_G(io)[PHPDBG_STDOUT] = stdout;
1377 		PHPDBG_G(io)[PHPDBG_STDERR] = stderr;
1378 
1379 		if (exec) { /* set execution context */
1380 			PHPDBG_G(exec) = phpdbg_resolve_path(exec TSRMLS_CC);
1381 			PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec));
1382 
1383 			free(exec);
1384 		}
1385 
1386 		if (oplog_file) { /* open oplog */
1387 			PHPDBG_G(oplog) = fopen(oplog_file, "w+");
1388 			if (!PHPDBG_G(oplog)) {
1389 				phpdbg_error(
1390 						"Failed to open oplog %s", oplog_file);
1391 			}
1392 			free(oplog_file);
1393 		}
1394 
1395 		/* set default colors */
1396 		phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT,  PHPDBG_STRL("white-bold") TSRMLS_CC);
1397 		phpdbg_set_color_ex(PHPDBG_COLOR_ERROR,   PHPDBG_STRL("red-bold") TSRMLS_CC);
1398 		phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE,  PHPDBG_STRL("green") TSRMLS_CC);
1399 
1400 		/* set default prompt */
1401 		phpdbg_set_prompt(PROMPT TSRMLS_CC);
1402 
1403 		/* Make stdin, stdout and stderr accessible from PHP scripts */
1404 		phpdbg_register_file_handles(TSRMLS_C);
1405 
1406 		if (show_banner) {
1407 			/* print blurb */
1408 			phpdbg_welcome((cleaning > 0) TSRMLS_CC);
1409 		}
1410 
1411 		/* auto compile */
1412 		if (PHPDBG_G(exec)) {
1413 			phpdbg_compile(TSRMLS_C);
1414 		}
1415 
1416 		/* initialize from file */
1417 		PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
1418 		zend_try {
1419 			phpdbg_init(init_file, init_file_len, init_file_default TSRMLS_CC);
1420 			phpdbg_try_file_init(bp_tmp_file, strlen(bp_tmp_file), 0 TSRMLS_CC);
1421 		} zend_end_try();
1422 		PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
1423 
1424 		/* quit if init says so */
1425 		if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
1426 			goto phpdbg_out;
1427 		}
1428 
1429 		/* step from here, not through init */
1430 		if (step) {
1431 			PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
1432 		}
1433 
1434 		if (run) {
1435 			/* no need to try{}, run does it ... */
1436 			PHPDBG_COMMAND_HANDLER(run)(NULL TSRMLS_CC);
1437 			if (run > 1) {
1438 				/* if -r is on the command line more than once just quit */
1439 				goto phpdbg_out;
1440 			}
1441 		}
1442 
1443 /* #ifndef for making compiler shutting up */
1444 #ifndef _WIN32
1445 phpdbg_interact:
1446 #endif
1447 		/* phpdbg main() */
1448 		do {
1449 			zend_try {
1450 				phpdbg_interactive(TSRMLS_C);
1451 			} zend_catch {
1452 				if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
1453 					FILE *bp_tmp_fp = fopen(bp_tmp_file, "w");
1454 					phpdbg_export_breakpoints(bp_tmp_fp TSRMLS_CC);
1455 					fclose(bp_tmp_fp);
1456 					cleaning = 1;
1457 				} else {
1458 					cleaning = 0;
1459 				}
1460 
1461 #ifndef _WIN32
1462 				if (!cleaning) {
1463 					/* remote client disconnected */
1464 					if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
1465 
1466 						if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
1467 							/* renegociate connections */
1468 							phpdbg_open_sockets(
1469 								address, listen, &server, &socket, streams);
1470 
1471 							/* set streams */
1472 							if (streams[0] && streams[1]) {
1473 								PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING;
1474 							}
1475 
1476 							/* this must be forced */
1477 							CG(unclean_shutdown) = 0;
1478 						} else {
1479 							/* local consoles cannot disconnect, ignore EOF */
1480 							PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
1481 						}
1482 					}
1483 				}
1484 #endif
1485 			} zend_end_try();
1486 		} while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
1487 
1488 		/* this must be forced */
1489 		CG(unclean_shutdown) = 0;
1490 
1491 		/* this is just helpful */
1492 		PG(report_memleaks) = 0;
1493 
1494 #ifndef _WIN32
1495 phpdbg_out:
1496 		if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
1497 			PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
1498 			goto phpdbg_interact;
1499 		}
1500 #endif
1501 
1502 #ifdef _WIN32
1503 	} __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
1504 		phpdbg_error("Access violation (Segmentation fault) encountered\ntrying to abort cleanly...");
1505 	}
1506 phpdbg_out:
1507 #endif
1508 
1509 		{
1510 			int i;
1511 			/* free argv */
1512 			for (i = SG(request_info).argc; --i;) {
1513 				efree(SG(request_info).argv[i]);
1514 			}
1515 			efree(SG(request_info).argv);
1516 		}
1517 
1518 #ifndef ZTS
1519 		/* force cleanup of auto and core globals */
1520 		zend_hash_clean(CG(auto_globals));
1521 		memset(
1522 			&core_globals, 0, sizeof(php_core_globals));
1523 #endif
1524 		if (ini_entries) {
1525 			free(ini_entries);
1526 		}
1527 
1528 		if (ini_override) {
1529 			free(ini_override);
1530 		}
1531 
1532 		/* this must be forced */
1533 		CG(unclean_shutdown) = 0;
1534 
1535 		/* this is just helpful */
1536 		PG(report_memleaks) = 0;
1537 
1538 		php_request_shutdown((void*)0);
1539 
1540 		zend_try {
1541 			php_module_shutdown(TSRMLS_C);
1542 		} zend_end_try();
1543 
1544 		sapi_shutdown();
1545 
1546 	}
1547 
1548 	if (cleaning || remote) {
1549 		goto phpdbg_main;
1550 	}
1551 
1552 #ifdef ZTS
1553 	/* bugggy */
1554 	/* tsrm_shutdown(); */
1555 #endif
1556 
1557 #ifndef _WIN32
1558 	if (address) {
1559 		free(address);
1560 	}
1561 #endif
1562 
1563 	if (sapi_name) {
1564 		free(sapi_name);
1565 	}
1566 
1567 #ifdef _WIN32
1568 	free(bp_tmp_file);
1569 #else
1570 	unlink(bp_tmp_file);
1571 #endif
1572 
1573 	return 0;
1574 } /* }}} */
1575