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