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