xref: /php-src/win32/signal.c (revision cf3b9fca)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Anatol Belski <ab@php.net>                                   |
14    +----------------------------------------------------------------------+
15 */
16 
17 #include "php.h"
18 #include "SAPI.h"
19 
20 #include "win32/console.h"
21 
22 /* true globals; only used from main thread and from kernel callback */
23 static zval ctrl_handler;
24 static DWORD ctrl_evt = (DWORD)-1;
25 static zend_atomic_bool *vm_interrupt_flag = NULL;
26 
27 static void (*orig_interrupt_function)(zend_execute_data *execute_data);
28 
php_win32_signal_ctrl_interrupt_function(zend_execute_data * execute_data)29 static void php_win32_signal_ctrl_interrupt_function(zend_execute_data *execute_data)
30 {/*{{{*/
31 	if (IS_UNDEF != Z_TYPE(ctrl_handler)) {
32 		zval retval, params[1];
33 
34 		ZVAL_LONG(&params[0], ctrl_evt);
35 
36 		/* If the function returns, */
37 		call_user_function(NULL, NULL, &ctrl_handler, &retval, 1, params);
38 		zval_ptr_dtor(&retval);
39 	}
40 
41 	if (orig_interrupt_function) {
42 		orig_interrupt_function(execute_data);
43 	}
44 }/*}}}*/
45 
php_win32_signal_ctrl_handler_init(void)46 PHP_WINUTIL_API void php_win32_signal_ctrl_handler_init(void)
47 {/*{{{*/
48 	/* We are in the main thread! */
49 	if (!php_win32_console_is_cli_sapi()) {
50 		return;
51 	}
52 
53 	orig_interrupt_function = zend_interrupt_function;
54 	zend_interrupt_function = php_win32_signal_ctrl_interrupt_function;
55 	vm_interrupt_flag = &EG(vm_interrupt);
56 	ZVAL_UNDEF(&ctrl_handler);
57 
58 	REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_EVENT_CTRL_C", CTRL_C_EVENT, CONST_PERSISTENT);
59 	REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_EVENT_CTRL_BREAK", CTRL_BREAK_EVENT, CONST_PERSISTENT);
60 }/*}}}*/
61 
php_win32_signal_ctrl_handler_shutdown(void)62 PHP_WINUTIL_API void php_win32_signal_ctrl_handler_shutdown(void)
63 {/*{{{*/
64 	if (!php_win32_console_is_cli_sapi()) {
65 		return;
66 	}
67 
68 	zend_interrupt_function = orig_interrupt_function;
69 	orig_interrupt_function = NULL;
70 	vm_interrupt_flag = NULL;
71 	ZVAL_UNDEF(&ctrl_handler);
72 }/*}}}*/
73 
php_win32_signal_system_ctrl_handler(DWORD evt)74 static BOOL WINAPI php_win32_signal_system_ctrl_handler(DWORD evt)
75 {/*{{{*/
76 	if (CTRL_C_EVENT != evt && CTRL_BREAK_EVENT != evt) {
77 		return FALSE;
78 	}
79 
80 	zend_atomic_bool_store_ex(vm_interrupt_flag, true);
81 
82 	ctrl_evt = evt;
83 
84 	return TRUE;
85 }/*}}}*/
86 
87 /* {{{ Assigns a CTRL signal handler to a PHP function */
PHP_FUNCTION(sapi_windows_set_ctrl_handler)88 PHP_FUNCTION(sapi_windows_set_ctrl_handler)
89 {
90 	zend_fcall_info fci;
91 	zend_fcall_info_cache fcc;
92 	bool add = 1;
93 
94 
95 	/* callable argument corresponds to the CTRL handler */
96 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!|b", &fci, &fcc, &add) == FAILURE) {
97 		RETURN_THROWS();
98 	}
99 
100 #ifdef ZTS
101 	if (!tsrm_is_main_thread()) {
102 		zend_throw_error(NULL, "CTRL events can only be received on the main thread");
103 		RETURN_THROWS();
104 	}
105 #endif
106 
107 	if (!php_win32_console_is_cli_sapi()) {
108 		zend_throw_error(NULL, "CTRL events trapping is only supported on console");
109 		RETURN_THROWS();
110 	}
111 
112 	if (!ZEND_FCI_INITIALIZED(fci)) {
113 		zval_ptr_dtor(&ctrl_handler);
114 		ZVAL_UNDEF(&ctrl_handler);
115 		if (!SetConsoleCtrlHandler(NULL, add)) {
116 			RETURN_FALSE;
117 		}
118 		RETURN_TRUE;
119 	}
120 
121 	if (!SetConsoleCtrlHandler(NULL, FALSE) || !SetConsoleCtrlHandler(php_win32_signal_system_ctrl_handler, add)) {
122 		zend_string *func_name = zend_get_callable_name(&fci.function_name);
123 		php_error_docref(NULL, E_WARNING, "Unable to attach %s as a CTRL handler", ZSTR_VAL(func_name));
124 		zend_string_release_ex(func_name, 0);
125 		RETURN_FALSE;
126 	}
127 
128 	zval_ptr_dtor_nogc(&ctrl_handler);
129 	ZVAL_COPY(&ctrl_handler, &fci.function_name);
130 
131 	RETURN_TRUE;
132 }/*}}}*/
133 
PHP_FUNCTION(sapi_windows_generate_ctrl_event)134 PHP_FUNCTION(sapi_windows_generate_ctrl_event)
135 {/*{{{*/
136 	zend_long evt, pid = 0;
137 	bool ret = 0;
138 
139 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &evt, &pid) == FAILURE) {
140 		RETURN_THROWS();
141 	}
142 
143 	if (!php_win32_console_is_cli_sapi()) {
144 		zend_throw_error(NULL, "CTRL events trapping is only supported on console");
145 		return;
146 	}
147 
148 	SetConsoleCtrlHandler(NULL, TRUE);
149 
150 	ret = (GenerateConsoleCtrlEvent(evt, pid) != 0);
151 
152 	if (IS_UNDEF != Z_TYPE(ctrl_handler)) {
153 		ret = ret && SetConsoleCtrlHandler(php_win32_signal_system_ctrl_handler, TRUE);
154 	}
155 
156 	RETURN_BOOL(ret);
157 }/*}}}*/
158