signal.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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. #include "php.h"
  17. #include "SAPI.h"
  18. #include "win32/console.h"
  19. /* true globals; only used from main thread and from kernel callback */
  20. static zval ctrl_handler;
  21. static DWORD ctrl_evt = (DWORD)-1;
  22. static bool *vm_interrupt_flag = NULL;
  23. static void (*orig_interrupt_function)(zend_execute_data *execute_data);
  24. static void php_win32_signal_ctrl_interrupt_function(zend_execute_data *execute_data)
  25. {/*{{{*/
  26. if (IS_UNDEF != Z_TYPE(ctrl_handler)) {
  27. zval retval, params[1];
  28. ZVAL_LONG(&params[0], ctrl_evt);
  29. /* If the function returns, */
  30. call_user_function(NULL, NULL, &ctrl_handler, &retval, 1, params);
  31. zval_ptr_dtor(&retval);
  32. }
  33. if (orig_interrupt_function) {
  34. orig_interrupt_function(execute_data);
  35. }
  36. }/*}}}*/
  37. PHP_WINUTIL_API void php_win32_signal_ctrl_handler_init(void)
  38. {/*{{{*/
  39. /* We are in the main thread! */
  40. if (!php_win32_console_is_cli_sapi()) {
  41. return;
  42. }
  43. orig_interrupt_function = zend_interrupt_function;
  44. zend_interrupt_function = php_win32_signal_ctrl_interrupt_function;
  45. vm_interrupt_flag = &EG(vm_interrupt);
  46. ZVAL_UNDEF(&ctrl_handler);
  47. REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_EVENT_CTRL_C", CTRL_C_EVENT, CONST_PERSISTENT | CONST_CS);
  48. REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_EVENT_CTRL_BREAK", CTRL_BREAK_EVENT, CONST_PERSISTENT | CONST_CS);
  49. }/*}}}*/
  50. PHP_WINUTIL_API void php_win32_signal_ctrl_handler_shutdown(void)
  51. {/*{{{*/
  52. if (!php_win32_console_is_cli_sapi()) {
  53. return;
  54. }
  55. zend_interrupt_function = orig_interrupt_function;
  56. orig_interrupt_function = NULL;
  57. vm_interrupt_flag = NULL;
  58. ZVAL_UNDEF(&ctrl_handler);
  59. }/*}}}*/
  60. static BOOL WINAPI php_win32_signal_system_ctrl_handler(DWORD evt)
  61. {/*{{{*/
  62. if (CTRL_C_EVENT != evt && CTRL_BREAK_EVENT != evt) {
  63. return FALSE;
  64. }
  65. (void)InterlockedExchange8(vm_interrupt_flag, 1);
  66. ctrl_evt = evt;
  67. return TRUE;
  68. }/*}}}*/
  69. /* {{{ Assigns a CTRL signal handler to a PHP function */
  70. PHP_FUNCTION(sapi_windows_set_ctrl_handler)
  71. {
  72. zend_fcall_info fci;
  73. zend_fcall_info_cache fcc;
  74. bool add = 1;
  75. /* callable argument corresponds to the CTRL handler */
  76. if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!|b", &fci, &fcc, &add) == FAILURE) {
  77. RETURN_THROWS();
  78. }
  79. #if ZTS
  80. if (!tsrm_is_main_thread()) {
  81. zend_throw_error(NULL, "CTRL events can only be received on the main thread");
  82. RETURN_THROWS();
  83. }
  84. #endif
  85. if (!php_win32_console_is_cli_sapi()) {
  86. zend_throw_error(NULL, "CTRL events trapping is only supported on console");
  87. RETURN_THROWS();
  88. }
  89. if (!ZEND_FCI_INITIALIZED(fci)) {
  90. zval_ptr_dtor(&ctrl_handler);
  91. ZVAL_UNDEF(&ctrl_handler);
  92. if (!SetConsoleCtrlHandler(NULL, add)) {
  93. RETURN_FALSE;
  94. }
  95. RETURN_TRUE;
  96. }
  97. if (!SetConsoleCtrlHandler(NULL, FALSE) || !SetConsoleCtrlHandler(php_win32_signal_system_ctrl_handler, add)) {
  98. zend_string *func_name = zend_get_callable_name(&fci.function_name);
  99. php_error_docref(NULL, E_WARNING, "Unable to attach %s as a CTRL handler", ZSTR_VAL(func_name));
  100. zend_string_release_ex(func_name, 0);
  101. RETURN_FALSE;
  102. }
  103. zval_ptr_dtor_nogc(&ctrl_handler);
  104. ZVAL_COPY(&ctrl_handler, &fci.function_name);
  105. RETURN_TRUE;
  106. }/*}}}*/
  107. PHP_FUNCTION(sapi_windows_generate_ctrl_event)
  108. {/*{{{*/
  109. zend_long evt, pid = 0;
  110. bool ret = 0;
  111. if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &evt, &pid) == FAILURE) {
  112. RETURN_THROWS();
  113. }
  114. if (!php_win32_console_is_cli_sapi()) {
  115. zend_throw_error(NULL, "CTRL events trapping is only supported on console");
  116. return;
  117. }
  118. SetConsoleCtrlHandler(NULL, TRUE);
  119. ret = (GenerateConsoleCtrlEvent(evt, pid) != 0);
  120. if (IS_UNDEF != Z_TYPE(ctrl_handler)) {
  121. ret = ret && SetConsoleCtrlHandler(php_win32_signal_system_ctrl_handler, TRUE);
  122. }
  123. RETURN_BOOL(ret);
  124. }/*}}}*/