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