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