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