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