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