diff options
-rw-r--r-- | ext/standard/basic_functions.c | 12 | ||||
-rw-r--r-- | ext/standard/basic_functions.h | 2 | ||||
-rw-r--r-- | ext/standard/proc_open.c | 11 | ||||
-rw-r--r-- | main/main.c | 2 | ||||
-rw-r--r-- | sapi/cli/tests/sapi_windows_set_ctrl_handler.phpt | 75 | ||||
-rw-r--r-- | win32/build/config.w32 | 2 | ||||
-rw-r--r-- | win32/codepage.c | 13 | ||||
-rw-r--r-- | win32/console.c | 8 | ||||
-rw-r--r-- | win32/console.h | 3 | ||||
-rw-r--r-- | win32/signal.c | 172 | ||||
-rw-r--r-- | win32/signal.h | 3 |
11 files changed, 294 insertions, 9 deletions
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 81c8449ab0..4189b0114f 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -2722,6 +2722,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sapi_windows_cp_conv, 0, 0, 3) ZEND_ARG_INFO(0, out_codepage) ZEND_ARG_TYPE_INFO(0, subject, IS_STRING, 0) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sapi_windows_set_ctrl_handler, 0, 0, 1) + ZEND_ARG_INFO(0, callable) + ZEND_ARG_INFO(0, add) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sapi_windows_generate_ctrl_event, 0, 0, 1) + ZEND_ARG_INFO(0, event) + ZEND_ARG_INFO(0, pid) +ZEND_END_ARG_INFO() #endif /* }}} */ /* }}} */ @@ -3437,6 +3447,8 @@ static const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(sapi_windows_cp_get, arginfo_sapi_windows_cp_get) PHP_FE(sapi_windows_cp_is_utf8, arginfo_sapi_windows_cp_is_utf8) PHP_FE(sapi_windows_cp_conv, arginfo_sapi_windows_cp_conv) + PHP_FE(sapi_windows_set_ctrl_handler, arginfo_sapi_windows_set_ctrl_handler) + PHP_FE(sapi_windows_generate_ctrl_event, arginfo_sapi_windows_generate_ctrl_event) #endif PHP_FE_END }; diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h index a0f3bfb120..2fff4e736e 100644 --- a/ext/standard/basic_functions.h +++ b/ext/standard/basic_functions.h @@ -138,6 +138,8 @@ PHP_FUNCTION(sapi_windows_cp_set); PHP_FUNCTION(sapi_windows_cp_get); PHP_FUNCTION(sapi_windows_cp_is_utf8); PHP_FUNCTION(sapi_windows_cp_conv); +PHP_FUNCTION(sapi_windows_set_ctrl_handler); +PHP_FUNCTION(sapi_windows_generate_ctrl_event); #endif PHP_FUNCTION(str_rot13); diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index d4cee29452..d8c7615d4b 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -438,6 +438,7 @@ PHP_FUNCTION(proc_open) int suppress_errors = 0; int bypass_shell = 0; int blocking_pipes = 0; + int create_process_group = 0; #endif #if PHP_CAN_DO_PTS php_file_descriptor_t dev_ptmx = -1; /* master */ @@ -478,6 +479,13 @@ PHP_FUNCTION(proc_open) blocking_pipes = 1; } } + + item = zend_hash_str_find(Z_ARRVAL_P(other_options), "create_process_group", sizeof("create_process_group") - 1); + if (item != NULL) { + if (Z_TYPE_P(item) == IS_TRUE || ((Z_TYPE_P(item) == IS_LONG) && Z_LVAL_P(item))) { + create_process_group = 1; + } + } } #endif @@ -716,6 +724,9 @@ PHP_FUNCTION(proc_open) if(strcmp(sapi_module.name, "cli") != 0) { dwCreateFlags |= CREATE_NO_WINDOW; } + if (create_process_group) { + dwCreateFlags |= CREATE_NEW_PROCESS_GROUP; + } envpw = php_win32_cp_env_any_to_w(env.envp); if (envpw) { diff --git a/main/main.c b/main/main.c index 3e472bf393..d59633f17a 100644 --- a/main/main.c +++ b/main/main.c @@ -2191,6 +2191,7 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod php_printf("\nwinsock.dll unusable. %d\n", WSAGetLastError()); return FAILURE; } + php_win32_signal_ctrl_handler_init(); #endif le_index_ptr = zend_register_list_destructors_ex(NULL, NULL, "index pointer", 0); @@ -2478,6 +2479,7 @@ void php_module_shutdown(void) #ifdef PHP_WIN32 (void)php_win32_shutdown_random_bytes(); + php_win32_signal_ctrl_handler_shutdown(); #endif sapi_flush(); diff --git a/sapi/cli/tests/sapi_windows_set_ctrl_handler.phpt b/sapi/cli/tests/sapi_windows_set_ctrl_handler.phpt new file mode 100644 index 0000000000..a5cb3ef03e --- /dev/null +++ b/sapi/cli/tests/sapi_windows_set_ctrl_handler.phpt @@ -0,0 +1,75 @@ +--TEST-- +sapi_windows_set_ctrl_handler() +--SKIPIF-- +<?php + +include "skipinf.inc"; + +if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') + die("skip this test is for Windows platforms only"); +?> +--FILE-- +<?php + +$is_child = isset($argv[1]); + + +if ($is_child) { + function handler($evt) + { + echo "\nCaught " . get_evt_name($evt), ", wait, wait ...!\n"; + exit; + } + + sapi_windows_set_ctrl_handler('handler'); + + while(1) usleep(100); +} else { + $cmd = PHP_BINARY . " -n " . $argv[0] . " 1"; + $spec = [0 => ["pipe", "r"], 1 => ["pipe", "w"]]; + + $proc = proc_open($cmd, $spec, $pipes, NULL, NULL, ["bypass_shell" => true, "create_process_group" => true]); + + if (!is_resource($proc)) { + die("Failed to start child. "); + } + + $child_pid = proc_get_status($proc)["pid"]; + echo "Started child $child_pid\n"; + usleep(300); + + $cmd = "tasklist /FI \"PID eq $child_pid\" /NH"; + echo "Running `$cmd` to check the process indeed exists:\n"; + echo trim(shell_exec($cmd)) . "\n"; + + $evt = PHP_WINDOWS_EVENT_CTRL_C; + echo "Sending ", get_evt_name($evt), " to child $child_pid\n"; + $ret = sapi_windows_generate_ctrl_event($evt, $child_pid); + + echo "Child said: \"", trim(fread($pipes[1], 48)), "\"\n"; + + echo ($ret ? "Successfully" : "Unsuccessfuly"), " sent ", get_evt_name($evt), " to child $child_pid\n"; + + proc_close($proc); +} + +function get_evt_name(int $evt) : ?string +{ + if (PHP_WINDOWS_EVENT_CTRL_C == $evt) { + return "CTRL+C"; + } if (PHP_WINDOWS_EVENT_CTRL_BREAK == $evt) { + return "CTRL+BREAK"; + } + + return NULL; +} + +?> +--EXPECTF-- +Started child %d +Running `tasklist /FI "PID eq %d" /NH` to check the process indeed exists: +php.exe%w%d%s1%s +Sending CTRL+C to child %d +Child said: "Caught CTRL+C, wait, wait ...!" +Successfully sent CTRL+C to child %d + diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 9fbbe8b7d2..8c281d4667 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -268,7 +268,7 @@ if (VS_TOOLSET && VCVERS >= 1914) { ADD_SOURCES("win32", "dllmain.c glob.c readdir.c \ registry.c select.c sendmail.c time.c winutil.c wsyslog.c globals.c \ getrusage.c ftok.c ioutil.c codepage.c nice.c \ - inet.c fnmatch.c sockets.c console.c"); + inet.c fnmatch.c sockets.c console.c signal.c"); ADD_FLAG("CFLAGS_BD_WIN32", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); if (VS_TOOLSET && VCVERS >= 1914) { diff --git a/win32/codepage.c b/win32/codepage.c index 839ba6d447..302fcb8b45 100644 --- a/win32/codepage.c +++ b/win32/codepage.c @@ -22,6 +22,8 @@ #include "SAPI.h" #include <emmintrin.h> +#include "win32/console.h" + ZEND_TLS const struct php_win32_cp *cur_cp = NULL; ZEND_TLS const struct php_win32_cp *orig_cp = NULL; ZEND_TLS const struct php_win32_cp *cur_out_cp = NULL; @@ -289,11 +291,6 @@ __forceinline static char *php_win32_cp_get_enc(void) return enc; }/*}}}*/ -__forceinline static BOOL php_win32_cp_is_cli_sapi() -{/*{{{*/ - return strlen(sapi_module.name) >= sizeof("cli") - 1 && !strncmp(sapi_module.name, "cli", sizeof("cli") - 1); -}/*}}}*/ - PW32CP const struct php_win32_cp *php_win32_cp_get_current(void) {/*{{{*/ return cur_cp; @@ -473,7 +470,7 @@ PW32CP const struct php_win32_cp *php_win32_cp_do_setup(const char *enc) if (!orig_cp) { orig_cp = php_win32_cp_get_by_id(GetACP()); } - if (php_win32_cp_is_cli_sapi()) { + if (php_win32_console_is_cli_sapi()) { if (!orig_in_cp) { orig_in_cp = php_win32_cp_get_by_id(GetConsoleCP()); if (!orig_in_cp) { @@ -499,7 +496,7 @@ PW32CP const struct php_win32_cp *php_win32_cp_do_update(const char *enc) } cur_cp = php_win32_cp_get_by_enc(enc); - if (php_win32_cp_is_cli_sapi()) { + if (php_win32_console_is_cli_sapi()) { php_win32_cp_cli_do_setup(cur_cp->id); } @@ -574,7 +571,7 @@ PHP_FUNCTION(sapi_windows_cp_set) RETURN_FALSE; } - if (php_win32_cp_is_cli_sapi()) { + if (php_win32_console_is_cli_sapi()) { cp = php_win32_cp_cli_do_setup((DWORD)id); } else { cp = php_win32_cp_set_by_id((DWORD)id); diff --git a/win32/console.c b/win32/console.c index 27dbd4548d..0b1bb0eff9 100644 --- a/win32/console.c +++ b/win32/console.c @@ -16,6 +16,8 @@ +----------------------------------------------------------------------+ */ +#include "php.h" +#include "SAPI.h" #include "win32/console.h" @@ -108,3 +110,9 @@ PHP_WINUTIL_API BOOL php_win32_console_is_own(void) return FALSE; }/*}}}*/ + +PHP_WINUTIL_API BOOL php_win32_console_is_cli_sapi(void) +{/*{{{*/ + return strlen(sapi_module.name) >= sizeof("cli") - 1 && !strncmp(sapi_module.name, "cli", sizeof("cli") - 1); +}/*}}}*/ + diff --git a/win32/console.h b/win32/console.h index 5e50eef8b1..2d36d85508 100644 --- a/win32/console.h +++ b/win32/console.h @@ -59,4 +59,7 @@ PHP_WINUTIL_API BOOL php_win32_console_fileno_set_vt100(zend_long fileno, BOOL e http://support.microsoft.com/kb/99115 */ PHP_WINUTIL_API BOOL php_win32_console_is_own(void); +/* Check whether the current SAPI is run on console. */ +PHP_WINUTIL_API BOOL php_win32_console_is_cli_sapi(void); + #endif diff --git a/win32/signal.c b/win32/signal.c new file mode 100644 index 0000000000..d682c42fbe --- /dev/null +++ b/win32/signal.c @@ -0,0 +1,172 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2019 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Anatol Belski <ab@php.net> | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "SAPI.h" + +#include "win32/console.h" + +ZEND_TLS zval ctrl_handler; +ZEND_TLS DWORD ctrl_evt = (DWORD)-1; +ZEND_TLS zend_bool *vm_interrupt_flag = NULL; + +static void (*orig_interrupt_function)(zend_execute_data *execute_data); + +static void php_win32_signal_ctrl_interrupt_function(zend_execute_data *execute_data) +{/*{{{*/ + if (IS_UNDEF != Z_TYPE(ctrl_handler)) { + zval retval, params[1]; + + ZVAL_LONG(¶ms[0], ctrl_evt); + + /* If the function returns, */ + call_user_function(EG(function_table), NULL, &ctrl_handler, &retval, 1, params); + zval_ptr_dtor(&retval); + } + + if (orig_interrupt_function) { + orig_interrupt_function(execute_data); + } +}/*}}}*/ + +PHP_WINUTIL_API void php_win32_signal_ctrl_handler_init(void) +{/*{{{*/ + /* We are in the main thread! */ + if (!php_win32_console_is_cli_sapi()) { + return; + } + + orig_interrupt_function = zend_interrupt_function; + zend_interrupt_function = php_win32_signal_ctrl_interrupt_function; + vm_interrupt_flag = &EG(vm_interrupt); + ZVAL_UNDEF(&ctrl_handler); + + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_EVENT_CTRL_C", CTRL_C_EVENT, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_EVENT_CTRL_BREAK", CTRL_BREAK_EVENT, CONST_PERSISTENT | CONST_CS); +}/*}}}*/ + +PHP_WINUTIL_API void php_win32_signal_ctrl_handler_shutdown(void) +{/*{{{*/ + if (!php_win32_console_is_cli_sapi()) { + return; + } + + zend_interrupt_function = orig_interrupt_function; + orig_interrupt_function = NULL; + vm_interrupt_flag = NULL; + ZVAL_UNDEF(&ctrl_handler); +}/*}}}*/ + +static BOOL php_win32_signal_system_ctrl_handler(DWORD evt) +{/*{{{*/ + if (CTRL_C_EVENT != evt && CTRL_BREAK_EVENT != evt) { + return FALSE; + } + + (void)InterlockedExchange((LONG*)vm_interrupt_flag, 1); + + ctrl_evt = evt; + + return TRUE; +}/*}}}*/ + +/* {{{ proto bool sapi_windows_set_ctrl_handler(callable handler, [, bool add = true]) + Assigns a CTRL signal handler to a PHP function */ +PHP_FUNCTION(sapi_windows_set_ctrl_handler) +{ + zval *handler = NULL; + zend_bool add = 1; + +#if ZTS + if (!tsrm_is_main_thread()) { + php_error_docref(NULL, E_WARNING, "CTRL events can only be received on the main thread"); + return; + } +#endif + + if (!php_win32_console_is_cli_sapi()) { + php_error_docref(NULL, E_WARNING, "CTRL events trapping is only supported on console"); + return; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &handler, &add) == FAILURE) { + return; + } + + if (IS_NULL == Z_TYPE_P(handler)) { + zval_dtor(&ctrl_handler); + ZVAL_UNDEF(&ctrl_handler); + if (!SetConsoleCtrlHandler(NULL, add)) { + RETURN_FALSE; + } + RETURN_TRUE; + } + + if (!zend_is_callable(handler, 0, NULL)) { + zend_string *func_name = zend_get_callable_name(handler); + php_error_docref(NULL, E_WARNING, "%s is not a callable function name error", ZSTR_VAL(func_name)); + zend_string_release_ex(func_name, 0); + RETURN_FALSE; + } + + if (!SetConsoleCtrlHandler(NULL, FALSE) || !SetConsoleCtrlHandler(php_win32_signal_system_ctrl_handler, add)) { + zend_string *func_name = zend_get_callable_name(handler); + php_error_docref(NULL, E_WARNING, "Unable to attach %s as a CTRL handler", ZSTR_VAL(func_name)); + zend_string_release_ex(func_name, 0); + RETURN_FALSE; + } + + zval_dtor(&ctrl_handler); + ZVAL_COPY(&ctrl_handler, handler); + + RETURN_TRUE; +}/*}}}*/ + +PHP_FUNCTION(sapi_windows_generate_ctrl_event) +{/*{{{*/ + zend_long evt, pid = 0; + zend_bool ret = 0; + + if (!php_win32_console_is_cli_sapi()) { + php_error_docref(NULL, E_WARNING, "CTRL events trapping is only supported on console"); + return; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &evt, &pid) == FAILURE) { + return; + } + + SetConsoleCtrlHandler(NULL, TRUE); + + ret = (GenerateConsoleCtrlEvent(evt, pid) != 0); + + if (IS_UNDEF != Z_TYPE(ctrl_handler)) { + ret = ret && SetConsoleCtrlHandler(php_win32_signal_system_ctrl_handler, TRUE); + } + + RETURN_BOOL(ret); +}/*}}}*/ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/signal.h b/win32/signal.h index ec739527ac..484a8d1c00 100644 --- a/win32/signal.h +++ b/win32/signal.h @@ -7,4 +7,7 @@ #define SIGVTALRM 26 /* virtual time alarm */ #define SIGPROF 27 /* profiling time alarm */ +PHP_WINUTIL_API void php_win32_signal_ctrl_handler_init(void); +PHP_WINUTIL_API void php_win32_signal_ctrl_handler_shutdown(void); + #endif /* PHP_WIN32_SIGNAL_H */ |