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