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