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