Skip to content

Commit 12bfd9a

Browse files
committed
Implement FR #77377 handle CTRL+C in Windows
1 parent e1dd8cd commit 12bfd9a

File tree

11 files changed

+294
-9
lines changed

11 files changed

+294
-9
lines changed

ext/standard/basic_functions.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2722,6 +2722,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sapi_windows_cp_conv, 0, 0, 3)
27222722
ZEND_ARG_INFO(0, out_codepage)
27232723
ZEND_ARG_TYPE_INFO(0, subject, IS_STRING, 0)
27242724
ZEND_END_ARG_INFO()
2725+
2726+
ZEND_BEGIN_ARG_INFO_EX(arginfo_sapi_windows_set_ctrl_handler, 0, 0, 1)
2727+
ZEND_ARG_INFO(0, callable)
2728+
ZEND_ARG_INFO(0, add)
2729+
ZEND_END_ARG_INFO()
2730+
2731+
ZEND_BEGIN_ARG_INFO_EX(arginfo_sapi_windows_generate_ctrl_event, 0, 0, 1)
2732+
ZEND_ARG_INFO(0, event)
2733+
ZEND_ARG_INFO(0, pid)
2734+
ZEND_END_ARG_INFO()
27252735
#endif
27262736
/* }}} */
27272737
/* }}} */
@@ -3437,6 +3447,8 @@ static const zend_function_entry basic_functions[] = { /* {{{ */
34373447
PHP_FE(sapi_windows_cp_get, arginfo_sapi_windows_cp_get)
34383448
PHP_FE(sapi_windows_cp_is_utf8, arginfo_sapi_windows_cp_is_utf8)
34393449
PHP_FE(sapi_windows_cp_conv, arginfo_sapi_windows_cp_conv)
3450+
PHP_FE(sapi_windows_set_ctrl_handler, arginfo_sapi_windows_set_ctrl_handler)
3451+
PHP_FE(sapi_windows_generate_ctrl_event, arginfo_sapi_windows_generate_ctrl_event)
34403452
#endif
34413453
PHP_FE_END
34423454
};

ext/standard/basic_functions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ PHP_FUNCTION(sapi_windows_cp_set);
138138
PHP_FUNCTION(sapi_windows_cp_get);
139139
PHP_FUNCTION(sapi_windows_cp_is_utf8);
140140
PHP_FUNCTION(sapi_windows_cp_conv);
141+
PHP_FUNCTION(sapi_windows_set_ctrl_handler);
142+
PHP_FUNCTION(sapi_windows_generate_ctrl_event);
141143
#endif
142144

143145
PHP_FUNCTION(str_rot13);

ext/standard/proc_open.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ PHP_FUNCTION(proc_open)
438438
int suppress_errors = 0;
439439
int bypass_shell = 0;
440440
int blocking_pipes = 0;
441+
int create_process_group = 0;
441442
#endif
442443
#if PHP_CAN_DO_PTS
443444
php_file_descriptor_t dev_ptmx = -1; /* master */
@@ -478,6 +479,13 @@ PHP_FUNCTION(proc_open)
478479
blocking_pipes = 1;
479480
}
480481
}
482+
483+
item = zend_hash_str_find(Z_ARRVAL_P(other_options), "create_process_group", sizeof("create_process_group") - 1);
484+
if (item != NULL) {
485+
if (Z_TYPE_P(item) == IS_TRUE || ((Z_TYPE_P(item) == IS_LONG) && Z_LVAL_P(item))) {
486+
create_process_group = 1;
487+
}
488+
}
481489
}
482490
#endif
483491

@@ -716,6 +724,9 @@ PHP_FUNCTION(proc_open)
716724
if(strcmp(sapi_module.name, "cli") != 0) {
717725
dwCreateFlags |= CREATE_NO_WINDOW;
718726
}
727+
if (create_process_group) {
728+
dwCreateFlags |= CREATE_NEW_PROCESS_GROUP;
729+
}
719730

720731
envpw = php_win32_cp_env_any_to_w(env.envp);
721732
if (envpw) {

main/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,6 +2191,7 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod
21912191
php_printf("\nwinsock.dll unusable. %d\n", WSAGetLastError());
21922192
return FAILURE;
21932193
}
2194+
php_win32_signal_ctrl_handler_init();
21942195
#endif
21952196

21962197
le_index_ptr = zend_register_list_destructors_ex(NULL, NULL, "index pointer", 0);
@@ -2478,6 +2479,7 @@ void php_module_shutdown(void)
24782479

24792480
#ifdef PHP_WIN32
24802481
(void)php_win32_shutdown_random_bytes();
2482+
php_win32_signal_ctrl_handler_shutdown();
24812483
#endif
24822484

24832485
sapi_flush();
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
--TEST--
2+
sapi_windows_set_ctrl_handler()
3+
--SKIPIF--
4+
<?php
5+
6+
include "skipinf.inc";
7+
8+
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
9+
die("skip this test is for Windows platforms only");
10+
?>
11+
--FILE--
12+
<?php
13+
14+
$is_child = isset($argv[1]);
15+
16+
17+
if ($is_child) {
18+
function handler($evt)
19+
{
20+
echo "\nCaught " . get_evt_name($evt), ", wait, wait ...!\n";
21+
exit;
22+
}
23+
24+
sapi_windows_set_ctrl_handler('handler');
25+
26+
while(1) usleep(100);
27+
} else {
28+
$cmd = PHP_BINARY . " -n " . $argv[0] . " 1";
29+
$spec = [0 => ["pipe", "r"], 1 => ["pipe", "w"]];
30+
31+
$proc = proc_open($cmd, $spec, $pipes, NULL, NULL, ["bypass_shell" => true, "create_process_group" => true]);
32+
33+
if (!is_resource($proc)) {
34+
die("Failed to start child. ");
35+
}
36+
37+
$child_pid = proc_get_status($proc)["pid"];
38+
echo "Started child $child_pid\n";
39+
usleep(300);
40+
41+
$cmd = "tasklist /FI \"PID eq $child_pid\" /NH";
42+
echo "Running `$cmd` to check the process indeed exists:\n";
43+
echo trim(shell_exec($cmd)) . "\n";
44+
45+
$evt = PHP_WINDOWS_EVENT_CTRL_C;
46+
echo "Sending ", get_evt_name($evt), " to child $child_pid\n";
47+
$ret = sapi_windows_generate_ctrl_event($evt, $child_pid);
48+
49+
echo "Child said: \"", trim(fread($pipes[1], 48)), "\"\n";
50+
51+
echo ($ret ? "Successfully" : "Unsuccessfuly"), " sent ", get_evt_name($evt), " to child $child_pid\n";
52+
53+
proc_close($proc);
54+
}
55+
56+
function get_evt_name(int $evt) : ?string
57+
{
58+
if (PHP_WINDOWS_EVENT_CTRL_C == $evt) {
59+
return "CTRL+C";
60+
} if (PHP_WINDOWS_EVENT_CTRL_BREAK == $evt) {
61+
return "CTRL+BREAK";
62+
}
63+
64+
return NULL;
65+
}
66+
67+
?>
68+
--EXPECTF--
69+
Started child %d
70+
Running `tasklist /FI "PID eq %d" /NH` to check the process indeed exists:
71+
php.exe%w%d%s1%s
72+
Sending CTRL+C to child %d
73+
Child said: "Caught CTRL+C, wait, wait ...!"
74+
Successfully sent CTRL+C to child %d
75+

win32/build/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ if (VS_TOOLSET && VCVERS >= 1914) {
268268
ADD_SOURCES("win32", "dllmain.c glob.c readdir.c \
269269
registry.c select.c sendmail.c time.c winutil.c wsyslog.c globals.c \
270270
getrusage.c ftok.c ioutil.c codepage.c nice.c \
271-
inet.c fnmatch.c sockets.c console.c");
271+
inet.c fnmatch.c sockets.c console.c signal.c");
272272

273273
ADD_FLAG("CFLAGS_BD_WIN32", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
274274
if (VS_TOOLSET && VCVERS >= 1914) {

win32/codepage.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "SAPI.h"
2323
#include <emmintrin.h>
2424

25+
#include "win32/console.h"
26+
2527
ZEND_TLS const struct php_win32_cp *cur_cp = NULL;
2628
ZEND_TLS const struct php_win32_cp *orig_cp = NULL;
2729
ZEND_TLS const struct php_win32_cp *cur_out_cp = NULL;
@@ -289,11 +291,6 @@ __forceinline static char *php_win32_cp_get_enc(void)
289291
return enc;
290292
}/*}}}*/
291293

292-
__forceinline static BOOL php_win32_cp_is_cli_sapi()
293-
{/*{{{*/
294-
return strlen(sapi_module.name) >= sizeof("cli") - 1 && !strncmp(sapi_module.name, "cli", sizeof("cli") - 1);
295-
}/*}}}*/
296-
297294
PW32CP const struct php_win32_cp *php_win32_cp_get_current(void)
298295
{/*{{{*/
299296
return cur_cp;
@@ -473,7 +470,7 @@ PW32CP const struct php_win32_cp *php_win32_cp_do_setup(const char *enc)
473470
if (!orig_cp) {
474471
orig_cp = php_win32_cp_get_by_id(GetACP());
475472
}
476-
if (php_win32_cp_is_cli_sapi()) {
473+
if (php_win32_console_is_cli_sapi()) {
477474
if (!orig_in_cp) {
478475
orig_in_cp = php_win32_cp_get_by_id(GetConsoleCP());
479476
if (!orig_in_cp) {
@@ -499,7 +496,7 @@ PW32CP const struct php_win32_cp *php_win32_cp_do_update(const char *enc)
499496
}
500497
cur_cp = php_win32_cp_get_by_enc(enc);
501498

502-
if (php_win32_cp_is_cli_sapi()) {
499+
if (php_win32_console_is_cli_sapi()) {
503500
php_win32_cp_cli_do_setup(cur_cp->id);
504501
}
505502

@@ -574,7 +571,7 @@ PHP_FUNCTION(sapi_windows_cp_set)
574571
RETURN_FALSE;
575572
}
576573

577-
if (php_win32_cp_is_cli_sapi()) {
574+
if (php_win32_console_is_cli_sapi()) {
578575
cp = php_win32_cp_cli_do_setup((DWORD)id);
579576
} else {
580577
cp = php_win32_cp_set_by_id((DWORD)id);

win32/console.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
+----------------------------------------------------------------------+
1717
*/
1818

19+
#include "php.h"
20+
#include "SAPI.h"
1921
#include "win32/console.h"
2022

2123

@@ -108,3 +110,9 @@ PHP_WINUTIL_API BOOL php_win32_console_is_own(void)
108110

109111
return FALSE;
110112
}/*}}}*/
113+
114+
PHP_WINUTIL_API BOOL php_win32_console_is_cli_sapi(void)
115+
{/*{{{*/
116+
return strlen(sapi_module.name) >= sizeof("cli") - 1 && !strncmp(sapi_module.name, "cli", sizeof("cli") - 1);
117+
}/*}}}*/
118+

win32/console.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,7 @@ PHP_WINUTIL_API BOOL php_win32_console_fileno_set_vt100(zend_long fileno, BOOL e
5959
http://support.microsoft.com/kb/99115 */
6060
PHP_WINUTIL_API BOOL php_win32_console_is_own(void);
6161

62+
/* Check whether the current SAPI is run on console. */
63+
PHP_WINUTIL_API BOOL php_win32_console_is_cli_sapi(void);
64+
6265
#endif

0 commit comments

Comments
 (0)