xref: /PHP-7.4/win32/signal.c (revision 637d8677)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 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    | Author: Anatol Belski <ab@php.net>                                   |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "php.h"
20 #include "SAPI.h"
21 
22 #include "win32/console.h"
23 
24 /* true globals; only used from main thread and from kernel callback */
25 static zval ctrl_handler;
26 static DWORD ctrl_evt = (DWORD)-1;
27 static zend_bool *vm_interrupt_flag = NULL;
28 
29 static void (*orig_interrupt_function)(zend_execute_data *execute_data);
30 
php_win32_signal_ctrl_interrupt_function(zend_execute_data * execute_data)31 static void php_win32_signal_ctrl_interrupt_function(zend_execute_data *execute_data)
32 {/*{{{*/
33 	if (IS_UNDEF != Z_TYPE(ctrl_handler)) {
34 		zval retval, params[1];
35 
36 		ZVAL_LONG(&params[0], ctrl_evt);
37 
38 		/* If the function returns, */
39 		call_user_function(NULL, NULL, &ctrl_handler, &retval, 1, params);
40 		zval_ptr_dtor(&retval);
41 	}
42 
43 	if (orig_interrupt_function) {
44 		orig_interrupt_function(execute_data);
45 	}
46 }/*}}}*/
47 
php_win32_signal_ctrl_handler_init(void)48 PHP_WINUTIL_API void php_win32_signal_ctrl_handler_init(void)
49 {/*{{{*/
50 	/* We are in the main thread! */
51 	if (!php_win32_console_is_cli_sapi()) {
52 		return;
53 	}
54 
55 	orig_interrupt_function = zend_interrupt_function;
56 	zend_interrupt_function = php_win32_signal_ctrl_interrupt_function;
57 	vm_interrupt_flag = &EG(vm_interrupt);
58 	ZVAL_UNDEF(&ctrl_handler);
59 
60 	REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_EVENT_CTRL_C", CTRL_C_EVENT, CONST_PERSISTENT | CONST_CS);
61 	REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_EVENT_CTRL_BREAK", CTRL_BREAK_EVENT, CONST_PERSISTENT | CONST_CS);
62 }/*}}}*/
63 
php_win32_signal_ctrl_handler_shutdown(void)64 PHP_WINUTIL_API void php_win32_signal_ctrl_handler_shutdown(void)
65 {/*{{{*/
66 	if (!php_win32_console_is_cli_sapi()) {
67 		return;
68 	}
69 
70 	zend_interrupt_function = orig_interrupt_function;
71 	orig_interrupt_function = NULL;
72 	vm_interrupt_flag = NULL;
73 	ZVAL_UNDEF(&ctrl_handler);
74 }/*}}}*/
75 
php_win32_signal_system_ctrl_handler(DWORD evt)76 static BOOL WINAPI php_win32_signal_system_ctrl_handler(DWORD evt)
77 {/*{{{*/
78 	if (CTRL_C_EVENT != evt && CTRL_BREAK_EVENT != evt) {
79 		return FALSE;
80 	}
81 
82 	(void)InterlockedExchange8(vm_interrupt_flag, 1);
83 
84 	ctrl_evt = evt;
85 
86 	return TRUE;
87 }/*}}}*/
88 
89 /* {{{ proto bool sapi_windows_set_ctrl_handler(callable handler, [, bool add = true])
90    Assigns a CTRL signal handler to a PHP function */
PHP_FUNCTION(sapi_windows_set_ctrl_handler)91 PHP_FUNCTION(sapi_windows_set_ctrl_handler)
92 {
93 	zval *handler = NULL;
94 	zend_bool add = 1;
95 
96 #if ZTS
97 	if (!tsrm_is_main_thread()) {
98 		php_error_docref(NULL, E_WARNING, "CTRL events can only be received on the main thread");
99 		return;
100 	}
101 #endif
102 
103 	if (!php_win32_console_is_cli_sapi()) {
104 		php_error_docref(NULL, E_WARNING, "CTRL events trapping is only supported on console");
105 		return;
106 	}
107 
108 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &handler, &add) == FAILURE) {
109 		return;
110 	}
111 
112 	if (IS_NULL == Z_TYPE_P(handler)) {
113 		zval_dtor(&ctrl_handler);
114 		ZVAL_UNDEF(&ctrl_handler);
115 		if (!SetConsoleCtrlHandler(NULL, add)) {
116 			RETURN_FALSE;
117 		}
118 		RETURN_TRUE;
119 	}
120 
121 	if (!zend_is_callable(handler, 0, NULL)) {
122 		zend_string *func_name = zend_get_callable_name(handler);
123 		php_error_docref(NULL, E_WARNING, "%s is not a callable function name error", ZSTR_VAL(func_name));
124 		zend_string_release_ex(func_name, 0);
125 		RETURN_FALSE;
126 	}
127 
128 	if (!SetConsoleCtrlHandler(NULL, FALSE) || !SetConsoleCtrlHandler(php_win32_signal_system_ctrl_handler, add)) {
129 		zend_string *func_name = zend_get_callable_name(handler);
130 		php_error_docref(NULL, E_WARNING, "Unable to attach %s as a CTRL handler", ZSTR_VAL(func_name));
131 		zend_string_release_ex(func_name, 0);
132 		RETURN_FALSE;
133 	}
134 
135 	zval_dtor(&ctrl_handler);
136 	ZVAL_COPY(&ctrl_handler, handler);
137 
138 	RETURN_TRUE;
139 }/*}}}*/
140 
PHP_FUNCTION(sapi_windows_generate_ctrl_event)141 PHP_FUNCTION(sapi_windows_generate_ctrl_event)
142 {/*{{{*/
143 	zend_long evt, pid = 0;
144 	zend_bool ret = 0;
145 
146 	if (!php_win32_console_is_cli_sapi()) {
147 		php_error_docref(NULL, E_WARNING, "CTRL events trapping is only supported on console");
148 		return;
149 	}
150 
151 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &evt, &pid) == FAILURE) {
152 		return;
153 	}
154 
155 	SetConsoleCtrlHandler(NULL, TRUE);
156 
157 	ret = (GenerateConsoleCtrlEvent(evt, pid) != 0);
158 
159 	if (IS_UNDEF != Z_TYPE(ctrl_handler)) {
160 		ret = ret && SetConsoleCtrlHandler(php_win32_signal_system_ctrl_handler, TRUE);
161 	}
162 
163 	RETURN_BOOL(ret);
164 }/*}}}*/
165