22 +----------------------------------------------------------------------+
33 | Xdebug |
44 +----------------------------------------------------------------------+
5- | Copyright (c) 2002-2024 Derick Rethans |
5+ | Copyright (c) 2002-2025 Derick Rethans |
66 +----------------------------------------------------------------------+
77 | This source file is subject to version 1.01 of the Xdebug license, |
88 | that is bundled with this package in the file LICENSE, and is |
1414 +----------------------------------------------------------------------+
1515 */
1616
17+ #ifdef __linux__
1718#include <stdio.h>
1819#include <stdlib.h>
1920#include <unistd.h>
2324#include <signal.h>
2425#include <string.h>
2526#include <errno.h>
27+ #endif
2628
2729#include "php_xdebug.h"
2830
3335#include "lib/log.h"
3436#include "lib/xml.h"
3537
38+ #if WIN32
39+ #include <windows.h>
40+ #endif
41+
3642ZEND_EXTERN_MODULE_GLOBALS (xdebug )
3743
3844typedef struct {
@@ -121,8 +127,11 @@ static xdebug_str *make_message(xdebug_xml_node *message)
121127 return ret ;
122128}
123129
124-
130+ #if __linux__
125131static void handle_command (int fd , const char * line )
132+ #elif WIN32
133+ static void handle_command (HANDLE h , const char * line )
134+ #endif
126135{
127136 char * cmd = NULL ;
128137 xdebug_dbgp_arg * args ;
@@ -147,7 +156,17 @@ static void handle_command(int fd, const char *line)
147156 }
148157
149158 message = make_message (retval );
159+ #if __linux__
150160 write (fd , message -> d , message -> l );
161+ #elif WIN32
162+ WriteFile (
163+ h ,
164+ message -> d ,
165+ message -> l ,
166+ NULL ,
167+ NULL
168+ );
169+ #endif
151170
152171 xdfree (cmd );
153172 xdebug_cmd_arg_dtor (args );
@@ -230,6 +249,7 @@ CTRL_FUNC(pause)
230249 xdebug_xml_add_child (* retval , response );
231250}
232251
252+ #if __linux__
233253static void xdebug_control_socket_handle (void )
234254{
235255 char buffer [256 ];
@@ -281,10 +301,88 @@ static void xdebug_control_socket_handle(void)
281301 close (new_sd );
282302 }
283303}
304+ #elif WIN32
305+ static void xdebug_control_socket_handle (void )
306+ {
307+ DWORD result ;
308+ char buffer [256 ];
309+ int bytes_read ;
284310
285- void xdebug_control_socket_dispatch (void )
311+ if (XG_BASE (control_socket_h ) <= 0 ) {
312+ // no NP
313+ return ;
314+ }
315+
316+ if (ConnectNamedPipe (XG_BASE (control_socket_h ), NULL )) {
317+ // previous disconnect
318+ DisconnectNamedPipe (XG_BASE (control_socket_h ));
319+ return ;
320+ }
321+
322+ result = GetLastError ();
323+
324+ if (result == ERROR_PIPE_LISTENING ) {
325+ // no clients
326+ return ;
327+ }
328+
329+ if (result == ERROR_NO_DATA ) {
330+ DisconnectNamedPipe (XG_BASE (control_socket_h ));
331+ return ;
332+ }
333+
334+ if (result == ERROR_PIPE_CONNECTED ) {
335+ // got new client!
336+ DWORD lpMode ;
337+ lpMode = PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS ;
338+ SetNamedPipeHandleState (XG_BASE (control_socket_h ), & lpMode , NULL , NULL );
339+
340+ memset (buffer , 0 , sizeof (buffer ));
341+ bytes_read = 0 ;
342+ if (!ReadFile (
343+ XG_BASE (control_socket_h ),
344+ buffer ,
345+ sizeof (buffer ),
346+ & bytes_read ,
347+ NULL
348+ )) {
349+ xdebug_log_ex (XLOG_CHAN_CONFIG , XLOG_WARN , "CTRL-RECV" , "Can't receive from NP: %x" , GetLastError ());
350+ } else {
351+ xdebug_log_ex (XLOG_CHAN_CONFIG , XLOG_INFO , "CTRL-RECV" , "Received: '%s'" , buffer );
352+ handle_command (0 , buffer );
353+ FlushFileBuffers (XG_BASE (control_socket_h ));
354+ }
355+
356+ lpMode = PIPE_TYPE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS ;
357+ SetNamedPipeHandleState (XG_BASE (control_socket_h ), & lpMode , NULL , NULL );
358+ }
359+
360+ // All other errors and completed reading should close the socket
361+ DisconnectNamedPipe (XG_BASE (control_socket_h ));
362+ }
363+ #endif
364+
365+ #if __linux__
366+ static bool is_control_socket_active (void )
286367{
287368 if (!XG_BASE (control_socket_path )) {
369+ return false;
370+ }
371+ return true;
372+ }
373+ #elif WIN32
374+ static bool is_control_socket_active (void )
375+ {
376+ if (XG_BASE (control_socket_h ) <= 0 ) {
377+ return false;
378+ }
379+ return true;
380+ }
381+ #endif
382+
383+ void xdebug_control_socket_dispatch (void )
384+ {
385+ if (!is_control_socket_active ()) {
288386 return ;
289387 }
290388
@@ -304,6 +402,7 @@ void xdebug_control_socket_dispatch(void)
304402 xdebug_control_socket_handle ();
305403}
306404
405+ #ifdef __linux__
307406void xdebug_control_socket_setup (void )
308407{
309408 struct sockaddr_un * servaddr = NULL ;
@@ -348,8 +447,7 @@ void xdebug_control_socket_setup(void)
348447 }
349448
350449 /* Part 3 — Listen */
351- if (listen (XG_BASE (control_socket_fd ), 32 ) < 0 )
352- {
450+ if (listen (XG_BASE (control_socket_fd ), 32 ) < 0 ) {
353451 xdebug_log_ex (XLOG_CHAN_CONFIG , XLOG_WARN , "CTRL-LISTEN" , "Listen failed: %s" , strerror (errno ));
354452 xdfree (servaddr );
355453 xdfree (XG_BASE (control_socket_path ));
@@ -370,5 +468,47 @@ void xdebug_control_socket_teardown(void)
370468 XG_BASE (control_socket_path ) = NULL ;
371469 }
372470}
471+ #elif WIN32
472+ void xdebug_control_socket_setup (void )
473+ {
474+ XG_BASE (control_socket_last_trigger ) = xdebug_get_nanotime ();
475+
476+ XG_BASE (control_socket_path ) = xdebug_sprintf ("\\\\.\\pipe\\xdebug-ctrl." ZEND_ULONG_FMT , xdebug_get_pid ());
477+
478+ /* Part 1 – create Named Pipe */
479+ XG_BASE (control_socket_h ) = CreateNamedPipeA (
480+ XG_BASE (control_socket_path ),
481+ PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE ,
482+ PIPE_TYPE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS ,
483+ 1 ,
484+ 1024 ,
485+ 1024 ,
486+ 0 ,
487+ NULL
488+ );
489+
490+ if (XG_BASE (control_socket_h ) == INVALID_HANDLE_VALUE ) {
491+ errno = WSAGetLastError ();
492+ xdebug_log_ex (XLOG_CHAN_CONFIG , XLOG_WARN , "CTRL-SOCKET" , "Can't create control Named Pipe (%x)" , errno );
493+ xdfree (XG_BASE (control_socket_path ));
494+ XG_BASE (control_socket_path ) = NULL ;
495+ return ;
496+ }
497+
498+ xdebug_log_ex (XLOG_CHAN_CONFIG , XLOG_INFO , "CTRL-OK" , "Control socket set up successfully: '%s'" , XG_BASE (control_socket_path ));
499+ }
500+
501+ void xdebug_control_socket_teardown (void )
502+ {
503+ if (XG_BASE (control_socket_path )) {
504+ xdfree (XG_BASE (control_socket_path ));
505+ XG_BASE (control_socket_path ) = NULL ;
506+ }
507+ if (XG_BASE (control_socket_h )) {
508+ DisconnectNamedPipe (XG_BASE (control_socket_h ));
509+ XG_BASE (control_socket_h ) = 0 ;
510+ }
511+ }
512+ #endif
373513
374514#endif
0 commit comments