summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/standard/basic_functions.c12
-rw-r--r--ext/standard/basic_functions.h2
-rw-r--r--ext/standard/proc_open.c11
-rw-r--r--main/main.c2
-rw-r--r--sapi/cli/tests/sapi_windows_set_ctrl_handler.phpt75
-rw-r--r--win32/build/config.w322
-rw-r--r--win32/codepage.c13
-rw-r--r--win32/console.c8
-rw-r--r--win32/console.h3
-rw-r--r--win32/signal.c172
-rw-r--r--win32/signal.h3
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(&params[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 */