1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
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        struct stat 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    {'V', 0, "version"},
765    {'-', 0, NULL}
766}; /* }}} */
767
768const char phpdbg_ini_hardcoded[] =
769"html_errors=Off\n"
770"register_argc_argv=On\n"
771"implicit_flush=On\n"
772"display_errors=Off\n"
773"log_errors=On\n"
774"max_execution_time=0\n"
775"max_input_time=-1\n"
776"error_log=\n"
777"output_buffering=off\n\0";
778
779/* overwriteable ini defaults must be set in phpdbg_ini_defaults() */
780#define INI_DEFAULT(name, value) \
781    ZVAL_STRINGL(&tmp, value, sizeof(value) - 1); \
782    Z_SET_REFCOUNT(tmp, 0); \
783    zend_hash_str_update(configuration_hash, name, sizeof(name) - 1, &tmp);
784
785void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */
786{
787    zval tmp;
788    INI_DEFAULT("report_zend_debug", "0");
789} /* }}} */
790
791static void phpdbg_welcome(zend_bool cleaning) /* {{{ */
792{
793    /* print blurb */
794    if (!cleaning) {
795        phpdbg_xml("<intros>");
796        phpdbg_notice("intro", "version=\"%s\"", "Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
797        phpdbg_writeln("intro", "help=\"help\"", "To get help using phpdbg type \"help\" and press enter");
798        phpdbg_notice("intro", "report=\"%s\"", "Please report bugs to <%s>", PHPDBG_ISSUES);
799        phpdbg_xml("</intros>");
800    } else if (phpdbg_startup_run == 0) {
801        if (!(PHPDBG_G(flags) & PHPDBG_WRITE_XML)) {
802            phpdbg_notice(NULL, NULL, "Clean Execution Environment");
803        }
804
805        phpdbg_write("cleaninfo", "classes=\"%d\" functions=\"%d\" constants=\"%d\" includes=\"%d\"",
806            "Classes              %d\n"
807            "Functions            %d\n"
808            "Constants            %d\n"
809            "Includes             %d\n",
810            zend_hash_num_elements(EG(class_table)),
811            zend_hash_num_elements(EG(function_table)),
812            zend_hash_num_elements(EG(zend_constants)),
813            zend_hash_num_elements(&EG(included_files)));
814    }
815} /* }}} */
816
817static inline void phpdbg_sigint_handler(int signo) /* {{{ */
818{
819
820    if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) {
821        /* we quit remote consoles on recv SIGINT */
822        if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
823            PHPDBG_G(flags) |= PHPDBG_IS_STOPPING;
824            zend_bailout();
825        }
826    } else {
827        /* set signalled only when not interactive */
828        if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
829            if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
830                char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
831
832                phpdbg_set_sigsafe_mem(mem);
833                zend_try {
834                    phpdbg_force_interruption();
835                } zend_end_try()
836                phpdbg_clear_sigsafe_mem();
837                return;
838            }
839            PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
840        }
841    }
842} /* }}} */
843
844static void phpdbg_remote_close(int socket, FILE *stream) {
845    if (socket >= 0) {
846        phpdbg_close_socket(socket);
847    }
848
849    if (stream) {
850        fclose(stream);
851    }
852}
853
854/* don't inline this, want to debug it easily, will inline when done */
855static int phpdbg_remote_init(const char* address, unsigned short port, int server, int *socket, FILE **stream) {
856    phpdbg_remote_close(*socket, *stream);
857
858    if (server < 0) {
859        phpdbg_rlog(fileno(stderr), "Initializing connection on %s:%u failed", address, port);
860
861        return FAILURE;
862    }
863
864    phpdbg_rlog(fileno(stderr), "accepting connections on %s:%u", address, port);
865    {
866        struct sockaddr_storage address;
867        socklen_t size = sizeof(address);
868        char buffer[20] = {0};
869        /* XXX error checks */
870        memset(&address, 0, size);
871        *socket = accept(server, (struct sockaddr *) &address, &size);
872        inet_ntop(AF_INET, &(((struct sockaddr_in *)&address)->sin_addr), buffer, sizeof(buffer));
873
874        phpdbg_rlog(fileno(stderr), "connection established from %s", buffer);
875    }
876
877#ifndef _WIN32
878    dup2(*socket, fileno(stdout));
879    dup2(*socket, fileno(stdin));
880
881    setbuf(stdout, NULL);
882
883    *stream = fdopen(*socket, "r+");
884
885    phpdbg_set_async_io(*socket);
886#endif
887    return SUCCESS;
888}
889
890#ifndef _WIN32
891/* This function *strictly* assumes that SIGIO is *only* used on the remote connection stream */
892void phpdbg_sigio_handler(int sig, siginfo_t *info, void *context) /* {{{ */
893{
894    int flags;
895    size_t newlen;
896    size_t i/*, last_nl*/;
897
898//  if (!(info->si_band & POLLIN)) {
899//      return; /* Not interested in writeablility etc., just interested in incoming data */
900//  }
901
902    /* only non-blocking reading, avoid non-blocking writing */
903    flags = fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_GETFL, 0);
904    fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags | O_NONBLOCK);
905
906    do {
907        char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
908        size_t off = 0;
909
910        if ((newlen = recv(PHPDBG_G(io)[PHPDBG_STDIN].fd, mem, PHPDBG_SIGSAFE_MEM_SIZE, MSG_PEEK)) == (size_t) -1) {
911            break;
912        }
913        for (i = 0; i < newlen; i++) {
914            switch (mem[off + i]) {
915                case '\x03': /* ^C char */
916                    if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) {
917                        break; /* or quit ??? */
918                    }
919                    if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
920                        phpdbg_set_sigsafe_mem(mem);
921                        zend_try {
922                            phpdbg_force_interruption();
923                        } zend_end_try();
924                        phpdbg_clear_sigsafe_mem();
925                        break;
926                    }
927                    if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
928                        PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
929                    }
930                    break;
931/*              case '\n':
932                    zend_llist_add_element(PHPDBG_G(stdin), strndup()
933                    last_nl = PHPDBG_G(stdin_buf).len + i;
934                    break;
935*/          }
936        }
937        off += i;
938    } while (0);
939
940
941    fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags);
942} /* }}} */
943
944void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
945{
946    int is_handled = FAILURE;
947
948    switch (sig) {
949        case SIGBUS:
950        case SIGSEGV:
951            if (PHPDBG_G(sigsegv_bailout)) {
952                LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
953            }
954            is_handled = phpdbg_watchpoint_segfault_handler(info, context);
955            if (is_handled == FAILURE) {
956#ifdef ZEND_SIGNALS
957                zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
958#else
959                sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
960#endif
961            }
962            break;
963    }
964
965} /* }}} */
966#endif
967
968static inline zend_mm_heap *phpdbg_mm_get_heap() /* {{{ */
969{
970    zend_mm_heap *mm_heap;
971
972
973    mm_heap = zend_mm_set_heap(NULL);
974    zend_mm_set_heap(mm_heap);
975
976    return mm_heap;
977} /* }}} */
978
979void *phpdbg_malloc_wrapper(size_t size) /* {{{ */
980{
981    return zend_mm_alloc(phpdbg_mm_get_heap(), size);
982} /* }}} */
983
984void phpdbg_free_wrapper(void *p) /* {{{ */
985{
986    zend_mm_free(phpdbg_mm_get_heap(), p);
987} /* }}} */
988
989void *phpdbg_realloc_wrapper(void *ptr, size_t size) /* {{{ */
990{
991    return zend_mm_realloc(phpdbg_mm_get_heap(), ptr, size);
992} /* }}} */
993
994int main(int argc, char **argv) /* {{{ */
995{
996    sapi_module_struct *phpdbg = &phpdbg_sapi_module;
997    char *sapi_name;
998    char *ini_entries;
999    int   ini_entries_len;
1000    char **zend_extensions = NULL;
1001    zend_ulong zend_extensions_len = 0L;
1002    zend_bool ini_ignore;
1003    char *ini_override;
1004    char *exec = NULL;
1005    char *init_file;
1006    size_t init_file_len;
1007    zend_bool init_file_default;
1008    char *oplog_file;
1009    size_t oplog_file_len;
1010    zend_ulong flags;
1011    char *php_optarg;
1012    int php_optind, opt, show_banner = 1;
1013    long cleaning = -1;
1014    zend_bool remote = 0;
1015    int step = 0;
1016    zend_phpdbg_globals *settings = NULL;
1017    char *bp_tmp = NULL;
1018    char *address;
1019    int listen = -1;
1020    int server = -1;
1021    int socket = -1;
1022    FILE* stream = NULL;
1023
1024#ifdef ZTS
1025    void ***tsrm_ls;
1026#endif
1027
1028#ifndef _WIN32
1029    struct sigaction sigio_struct;
1030    struct sigaction signal_struct;
1031    signal_struct.sa_sigaction = phpdbg_signal_handler;
1032    signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
1033    sigio_struct.sa_sigaction = phpdbg_sigio_handler;
1034    sigio_struct.sa_flags = SA_SIGINFO;
1035
1036
1037    address = strdup("127.0.0.1");
1038#endif
1039
1040#ifdef PHP_WIN32
1041    _fmode = _O_BINARY;                 /* sets default for file streams to binary */
1042    setmode(_fileno(stdin), O_BINARY);  /* make the stdio mode be binary */
1043    setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
1044    setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
1045#endif
1046
1047#ifdef ZTS
1048    tsrm_startup(1, 1, 0, NULL);
1049
1050    tsrm_ls = ts_resource(0);
1051#endif
1052
1053phpdbg_main:
1054    ini_entries = NULL;
1055    ini_entries_len = 0;
1056    ini_ignore = 0;
1057    ini_override = NULL;
1058    zend_extensions = NULL;
1059    zend_extensions_len = 0L;
1060    init_file = NULL;
1061    init_file_len = 0;
1062    init_file_default = 1;
1063    oplog_file = NULL;
1064    oplog_file_len = 0;
1065    flags = PHPDBG_DEFAULT_FLAGS;
1066    php_optarg = NULL;
1067    php_optind = 1;
1068    opt = 0;
1069    step = 0;
1070    sapi_name = NULL;
1071    if (settings) {
1072        exec = settings->exec;
1073    }
1074
1075    while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
1076        switch (opt) {
1077            case 'r':
1078                phpdbg_startup_run++;
1079                break;
1080            case 'n':
1081                ini_ignore = 1;
1082                break;
1083            case 'c':
1084                if (ini_override) {
1085                    free(ini_override);
1086                }
1087                ini_override = strdup(php_optarg);
1088                break;
1089            case 'd': {
1090                int len = strlen(php_optarg);
1091                char *val;
1092
1093                if ((val = strchr(php_optarg, '='))) {
1094                  val++;
1095                  if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
1096                      ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
1097                      memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
1098                      ini_entries_len += (val - php_optarg);
1099                      memcpy(ini_entries + ini_entries_len, "\"", 1);
1100                      ini_entries_len++;
1101                      memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg));
1102                      ini_entries_len += len - (val - php_optarg);
1103                      memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
1104                      ini_entries_len += sizeof("\n\0\"") - 2;
1105                  } else {
1106                      ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0"));
1107                      memcpy(ini_entries + ini_entries_len, php_optarg, len);
1108                      memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
1109                      ini_entries_len += len + sizeof("\n\0") - 2;
1110                  }
1111                } else {
1112                  ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
1113                  memcpy(ini_entries + ini_entries_len, php_optarg, len);
1114                  memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
1115                  ini_entries_len += len + sizeof("=1\n\0") - 2;
1116                }
1117            } break;
1118
1119            case 'z':
1120                zend_extensions_len++;
1121                if (zend_extensions) {
1122                    zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len);
1123                } else zend_extensions = malloc(sizeof(char*) * zend_extensions_len);
1124                zend_extensions[zend_extensions_len-1] = strdup(php_optarg);
1125            break;
1126
1127            /* begin phpdbg options */
1128
1129            case 'S': { /* set SAPI name */
1130                if (sapi_name) {
1131                    free(sapi_name);
1132                }
1133                sapi_name = strdup(php_optarg);
1134            } break;
1135
1136            case 'I': { /* ignore .phpdbginit */
1137                init_file_default = 0;
1138            } break;
1139
1140            case 'i': { /* set init file */
1141                if (init_file) {
1142                    free(init_file);
1143                }
1144
1145                init_file_len = strlen(php_optarg);
1146                if (init_file_len) {
1147                    init_file = strdup(php_optarg);
1148                }
1149            } break;
1150
1151            case 'O': { /* set oplog output */
1152                oplog_file_len = strlen(php_optarg);
1153                if (oplog_file_len) {
1154                    oplog_file = strdup(php_optarg);
1155                }
1156            } break;
1157
1158            case 'v': /* set quietness off */
1159                flags &= ~PHPDBG_IS_QUIET;
1160            break;
1161
1162            case 's': /* set stepping on */
1163                step = 1;
1164            break;
1165
1166            case 'E': /* stepping through eval on */
1167                flags |= PHPDBG_IS_STEPONEVAL;
1168            break;
1169
1170            case 'b': /* set colours off */
1171                flags &= ~PHPDBG_IS_COLOURED;
1172            break;
1173
1174            case 'q': /* hide banner */
1175                show_banner = 0;
1176            break;
1177
1178#ifndef _WIN32
1179            /* if you pass a listen port, we will read and write on listen port */
1180            case 'l': /* set listen ports */
1181                if (sscanf(php_optarg, "%d", &listen) != 1) {
1182                    listen = 8000;
1183                }
1184                break;
1185
1186            case 'a': { /* set bind address */
1187                free(address);
1188                if (!php_optarg) {
1189                    address = strdup("*");
1190                } else address = strdup(php_optarg);
1191            } break;
1192#endif
1193
1194            case 'x':
1195                flags |= PHPDBG_WRITE_XML;
1196            break;
1197
1198            case 'V': {
1199                sapi_startup(phpdbg);
1200                phpdbg->startup(phpdbg);
1201                printf(
1202                    "phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) 1997-2015 The PHP Group\n%s",
1203                    PHPDBG_VERSION,
1204                    __DATE__,
1205                    __TIME__,
1206                    PHP_VERSION,
1207                    get_zend_version()
1208                );
1209                sapi_deactivate();
1210                sapi_shutdown();
1211                return 0;
1212            } break;
1213        }
1214    }
1215
1216    /* set exec if present on command line */
1217    if (!exec && (argc > php_optind) && (strcmp(argv[php_optind-1], "--") != SUCCESS)) {
1218        if (strlen(argv[php_optind])) {
1219            if (exec) {
1220                free(exec);
1221            }
1222            exec = strdup(argv[php_optind]);
1223        }
1224        php_optind++;
1225    }
1226
1227    if (sapi_name) {
1228        phpdbg->name = sapi_name;
1229    }
1230
1231    phpdbg->ini_defaults = phpdbg_ini_defaults;
1232    phpdbg->phpinfo_as_text = 1;
1233    phpdbg->php_ini_ignore_cwd = 1;
1234
1235    sapi_startup(phpdbg);
1236
1237    phpdbg->executable_location = argv[0];
1238    phpdbg->phpinfo_as_text = 1;
1239    phpdbg->php_ini_ignore = ini_ignore;
1240    phpdbg->php_ini_path_override = ini_override;
1241
1242    if (ini_entries) {
1243        ini_entries = realloc(ini_entries, ini_entries_len + sizeof(phpdbg_ini_hardcoded));
1244        memmove(ini_entries + sizeof(phpdbg_ini_hardcoded) - 2, ini_entries, ini_entries_len + 1);
1245        memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded) - 2);
1246    } else {
1247        ini_entries = malloc(sizeof(phpdbg_ini_hardcoded));
1248        memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded));
1249    }
1250    ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2;
1251
1252    if (zend_extensions_len) {
1253        zend_ulong zend_extension = 0L;
1254
1255        while (zend_extension < zend_extensions_len) {
1256            const char *ze = zend_extensions[zend_extension];
1257            size_t ze_len = strlen(ze);
1258
1259            ini_entries = realloc(
1260                ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n"))));
1261            memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1));
1262            ini_entries_len += (sizeof("zend_extension=")-1);
1263            memcpy(&ini_entries[ini_entries_len], ze, ze_len);
1264            ini_entries_len += ze_len;
1265            memcpy(&ini_entries[ini_entries_len], "\n", (sizeof("\n") - 1));
1266
1267            free(zend_extensions[zend_extension]);
1268            zend_extension++;
1269        }
1270
1271        free(zend_extensions);
1272    }
1273
1274    phpdbg->ini_entries = ini_entries;
1275
1276    if (phpdbg->startup(phpdbg) == SUCCESS) {
1277#ifdef _WIN32
1278    EXCEPTION_POINTERS *xp;
1279    __try {
1280#endif
1281        zend_mm_heap *mm_heap;
1282        void* (*_malloc)(size_t);
1283        void (*_free)(void*);
1284        void* (*_realloc)(void*, size_t);
1285
1286        /* set flags from command line */
1287        PHPDBG_G(flags) = flags;
1288
1289        if (settings) {
1290#ifdef ZTS
1291            *((zend_phpdbg_globals *) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(phpdbg_globals_id)]) = *settings;
1292#else
1293            phpdbg_globals = *settings;
1294#endif
1295        }
1296
1297        /* setup remote server if necessary */
1298        if (cleaning <= 0 && listen > 0) {
1299            server = phpdbg_open_socket(address, listen);
1300                if (-1 > server || phpdbg_remote_init(address, listen, server, &socket, &stream) == FAILURE) {
1301                exit(0);
1302            }
1303
1304#ifndef _WIN32
1305            sigaction(SIGIO, &sigio_struct, NULL);
1306#endif
1307
1308            /* set remote flag to stop service shutting down upon quit */
1309            remote = 1;
1310        }
1311
1312        mm_heap = phpdbg_mm_get_heap();
1313        zend_mm_get_custom_handlers(mm_heap, &_malloc, &_free, &_realloc);
1314
1315        if (!_malloc) {
1316            _malloc = phpdbg_malloc_wrapper;
1317        }
1318        if (!_realloc) {
1319            _realloc = phpdbg_realloc_wrapper;
1320        }
1321        if (!_free) {
1322            _free = phpdbg_free_wrapper;
1323        }
1324
1325        zend_activate();
1326
1327        phpdbg_init_list();
1328
1329        PHPDBG_G(original_free_function) = _free;
1330        _free = phpdbg_watch_efree;
1331
1332        zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc);
1333
1334        phpdbg_setup_watchpoints();
1335
1336#if defined(ZEND_SIGNALS) && !defined(_WIN32)
1337        zend_try {
1338            zend_signal_activate();
1339        } zend_end_try();
1340#endif
1341
1342#if defined(ZEND_SIGNALS) && !defined(_WIN32)
1343        zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1344        zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
1345#elif !defined(_WIN32)
1346        sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
1347        sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
1348#endif
1349
1350        PHPDBG_G(sapi_name_ptr) = sapi_name;
1351
1352        php_output_activate();
1353        php_output_deactivate();
1354
1355        php_output_activate();
1356
1357        if (php_request_startup() == SUCCESS) {
1358            int i;
1359
1360            SG(request_info).argc = argc - php_optind + 1;
1361            SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *));
1362            for (i = SG(request_info).argc; --i;) {
1363                SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]);
1364            }
1365            SG(request_info).argv[i] = exec ? estrdup(exec) : estrdup("");
1366
1367            php_hash_environment();
1368        }
1369
1370        /* do not install sigint handlers for remote consoles */
1371        /* sending SIGINT then provides a decent way of shutting down the server */
1372#ifndef _WIN32
1373        if (listen < 0) {
1374#endif
1375#if defined(ZEND_SIGNALS) && !defined(_WIN32)
1376            zend_try { zend_signal(SIGINT, phpdbg_sigint_handler); } zend_end_try();
1377#else
1378            signal(SIGINT, phpdbg_sigint_handler);
1379#endif
1380#ifndef _WIN32
1381        }
1382#endif
1383
1384        PG(modules_activated) = 0;
1385
1386#ifndef _WIN32
1387        /* setup io here */
1388        if (remote) {
1389            PHPDBG_G(flags) |= PHPDBG_IS_REMOTE;
1390
1391            signal(SIGPIPE, SIG_IGN);
1392        }
1393#endif
1394
1395#ifndef _WIN32
1396        PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
1397        PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
1398        PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1399        PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1400#else
1401        /* XXX this is a complete mess here with FILE/fd/SOCKET,
1402            we should let only one to survive probably. Need
1403            a clean separation whether it's a remote or local
1404            prompt. And what is supposed to go as user interaction,
1405            error log, etc. */
1406        if (remote) {
1407            PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
1408            PHPDBG_G(io)[PHPDBG_STDIN].fd = socket;
1409            PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1410            PHPDBG_G(io)[PHPDBG_STDOUT].fd = socket;
1411        } else {
1412            PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
1413            PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
1414            PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
1415            PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
1416        }
1417#endif
1418        PHPDBG_G(io)[PHPDBG_STDERR].ptr = stderr;
1419        PHPDBG_G(io)[PHPDBG_STDERR].fd = fileno(stderr);
1420
1421#ifndef _WIN32
1422        PHPDBG_G(php_stdiop_write) = php_stream_stdio_ops.write;
1423        php_stream_stdio_ops.write = phpdbg_stdiop_write;
1424#endif
1425
1426        if (exec) { /* set execution context */
1427            PHPDBG_G(exec) = phpdbg_resolve_path(exec);
1428            PHPDBG_G(exec_len) = PHPDBG_G(exec) ? strlen(PHPDBG_G(exec)) : 0;
1429
1430            free(exec);
1431            exec = NULL;
1432        }
1433
1434        if (oplog_file) { /* open oplog */
1435            PHPDBG_G(oplog) = fopen(oplog_file, "w+");
1436            if (!PHPDBG_G(oplog)) {
1437                phpdbg_error("oplog", "path=\"%s\"", "Failed to open oplog %s", oplog_file);
1438            }
1439            free(oplog_file);
1440        }
1441
1442        /* set default colors */
1443        phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT,  PHPDBG_STRL("white-bold"));
1444        phpdbg_set_color_ex(PHPDBG_COLOR_ERROR,   PHPDBG_STRL("red-bold"));
1445        phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE,  PHPDBG_STRL("green"));
1446
1447        /* set default prompt */
1448        phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT);
1449
1450        /* Make stdin, stdout and stderr accessible from PHP scripts */
1451        phpdbg_register_file_handles();
1452
1453        if (show_banner && cleaning < 2) {
1454            /* print blurb */
1455            phpdbg_welcome(cleaning == 1);
1456        }
1457
1458        cleaning = -1;
1459
1460        /* initialize from file */
1461        PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
1462        zend_try {
1463            phpdbg_init(init_file, init_file_len, init_file_default);
1464            if (bp_tmp) {
1465                PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1466                phpdbg_string_init(bp_tmp);
1467                free(bp_tmp);
1468                bp_tmp = NULL;
1469                PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1470            }
1471        } zend_end_try();
1472        PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING;
1473
1474        /* quit if init says so */
1475        if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
1476            goto phpdbg_out;
1477        }
1478
1479        /* auto compile */
1480        if (PHPDBG_G(exec)) {
1481            if (settings) {
1482                PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1483            }
1484
1485            zend_try {
1486                phpdbg_compile();
1487            } zend_end_try();
1488
1489            PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1490        }
1491
1492        /* step from here, not through init */
1493        if (step) {
1494            PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
1495        }
1496
1497        phpdbg_fully_started = 1;
1498
1499/* #ifndef for making compiler shutting up */
1500#ifndef _WIN32
1501phpdbg_interact:
1502#endif
1503
1504        /* phpdbg main() */
1505        do {
1506            zend_try {
1507                if (phpdbg_startup_run) {
1508                    zend_bool quit_immediately = phpdbg_startup_run > 1;
1509                    phpdbg_startup_run = 0;
1510                    PHPDBG_COMMAND_HANDLER(run)(NULL);
1511                    if (quit_immediately) {
1512                        /* if -r is on the command line more than once just quit */
1513                        EG(bailout) = __orig_bailout; /* reset zend_try */
1514                        break;
1515                    }
1516                }
1517
1518                phpdbg_interactive(1);
1519            } zend_catch {
1520                if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
1521                    char *bp_tmp_str;
1522                    PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT;
1523                    phpdbg_export_breakpoints_to_string(&bp_tmp_str);
1524                    PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT;
1525                    if (bp_tmp_str) {
1526                        bp_tmp = strdup(bp_tmp_str);
1527                        efree(bp_tmp_str);
1528                    }
1529                    cleaning = 1;
1530                } else {
1531                    cleaning = 0;
1532                }
1533
1534#ifndef _WIN32
1535                if (!cleaning) {
1536                    /* remote client disconnected */
1537                    if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
1538
1539                        if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
1540                            /* renegociate connections */
1541                            phpdbg_remote_init(address, listen, server, &socket, &stream);
1542
1543                            /* set streams */
1544                            if (stream) {
1545                                PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING;
1546                            }
1547
1548                            /* this must be forced */
1549                            CG(unclean_shutdown) = 0;
1550                        } else {
1551                            /* local consoles cannot disconnect, ignore EOF */
1552                            PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
1553                        }
1554                    }
1555                }
1556#endif
1557            } zend_end_try();
1558        } while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING));
1559
1560
1561        if (PHPDBG_G(exec) && (PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
1562            exec = strdup(PHPDBG_G(exec)); /* preserve exec, don't reparse that from cmd */
1563        }
1564
1565        /* this must be forced */
1566        CG(unclean_shutdown) = 0;
1567
1568        /* this is just helpful */
1569        PG(report_memleaks) = 0;
1570
1571#ifndef _WIN32
1572phpdbg_out:
1573        if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
1574            PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
1575            goto phpdbg_interact;
1576        }
1577#endif
1578
1579#ifdef _WIN32
1580    } __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
1581        phpdbg_error("segfault", "", "Access violation (Segmentation fault) encountered\ntrying to abort cleanly...");
1582    }
1583phpdbg_out:
1584#endif
1585
1586        if (cleaning <= 0) {
1587            PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING;
1588            cleaning = -1;
1589        }
1590
1591        {
1592            int i;
1593            /* free argv */
1594            for (i = SG(request_info).argc; --i;) {
1595                efree(SG(request_info).argv[i]);
1596            }
1597            efree(SG(request_info).argv);
1598        }
1599
1600#ifndef _WIN32
1601        /* reset it... else we risk a stack overflow upon next run (when clean'ing) */
1602        php_stream_stdio_ops.write = PHPDBG_G(php_stdiop_write);
1603#endif
1604
1605#ifndef ZTS
1606        /* force cleanup of auto and core globals */
1607        zend_hash_clean(CG(auto_globals));
1608        memset( &core_globals, 0, sizeof(php_core_globals));
1609#endif
1610        if (ini_entries) {
1611            free(ini_entries);
1612        }
1613
1614        if (ini_override) {
1615            free(ini_override);
1616        }
1617
1618        /* this must be forced */
1619        CG(unclean_shutdown) = 0;
1620
1621        /* this is just helpful */
1622        PG(report_memleaks) = 0;
1623
1624        if ((PHPDBG_G(flags) & (PHPDBG_IS_CLEANING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_CLEANING) {
1625            php_free_shutdown_functions();
1626            zend_objects_store_mark_destructed(&EG(objects_store));
1627        }
1628
1629        /* sapi_module.deactivate is where to backup things, last chance before mm_shutdown... */
1630
1631        zend_try {
1632            php_request_shutdown(NULL);
1633        } zend_end_try();
1634
1635        if ((PHPDBG_G(flags) & (PHPDBG_IS_QUITTING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_RUNNING) {
1636            phpdbg_notice("stop", "type=\"normal\"", "Script ended normally");
1637            cleaning++;
1638        }
1639
1640        if ((PHPDBG_G(flags) & PHPDBG_IS_STOPPING) == PHPDBG_IS_CLEANING) {
1641            settings = PHPDBG_G(backup);
1642        }
1643
1644        php_output_deactivate();
1645
1646        zend_try {
1647            php_module_shutdown();
1648        } zend_end_try();
1649
1650        sapi_shutdown();
1651
1652    }
1653
1654    if (cleaning > 0 || remote) {
1655        goto phpdbg_main;
1656    }
1657
1658#ifdef ZTS
1659    /* bugggy */
1660    /* tsrm_shutdown(); */
1661#endif
1662
1663#ifndef _WIN32
1664    if (address) {
1665        free(address);
1666    }
1667#endif
1668
1669    if (PHPDBG_G(sapi_name_ptr)) {
1670        free(PHPDBG_G(sapi_name_ptr));
1671    }
1672
1673    return 0;
1674} /* }}} */
1675