@@ -444,11 +444,18 @@ static inline HANDLE dup_fd_as_handle(int fd)
444444# define close_descriptor (fd ) close(fd)
445445#endif
446446
447+ /* Determines the type of a descriptor item. */
448+ typedef enum _descriptor_type {
449+ DESCRIPTOR_TYPE_STD ,
450+ DESCRIPTOR_TYPE_PIPE ,
451+ DESCRIPTOR_TYPE_SOCKET
452+ } descriptor_type ;
453+
447454/* One instance of this struct is created for each item in `$descriptorspec` argument to `proc_open`
448455 * They are used within `proc_open` and freed before it returns */
449456typedef struct _descriptorspec_item {
450457 int index ; /* desired FD # in child process */
451- int is_pipe ;
458+ descriptor_type type ;
452459 php_file_descriptor_t childend ; /* FD # opened for use in child
453460 * (will be copied to `index` in child) */
454461 php_file_descriptor_t parentend ; /* FD # opened for use in parent
@@ -679,7 +686,7 @@ static int set_proc_descriptor_to_pty(descriptorspec_item *desc, int *master_fd,
679686 }
680687 }
681688
682- desc -> is_pipe = 1 ;
689+ desc -> type = DESCRIPTOR_TYPE_PIPE ;
683690 desc -> childend = dup (* slave_fd );
684691 desc -> parentend = dup (* master_fd );
685692 desc -> mode_flags = O_RDWR ;
@@ -690,6 +697,19 @@ static int set_proc_descriptor_to_pty(descriptorspec_item *desc, int *master_fd,
690697#endif
691698}
692699
700+ /* Mark the descriptor close-on-exec, so it won't be inherited by children */
701+ static php_file_descriptor_t make_descriptor_cloexec (php_file_descriptor_t fd )
702+ {
703+ #ifdef PHP_WIN32
704+ return dup_handle (fd , FALSE, TRUE);
705+ #else
706+ #if defined(F_SETFD ) && defined(FD_CLOEXEC )
707+ fcntl (fd , F_SETFD , FD_CLOEXEC );
708+ #endif
709+ return fd ;
710+ #endif
711+ }
712+
693713static int set_proc_descriptor_to_pipe (descriptorspec_item * desc , zend_string * zmode )
694714{
695715 php_file_descriptor_t newpipe [2 ];
@@ -699,7 +719,7 @@ static int set_proc_descriptor_to_pipe(descriptorspec_item *desc, zend_string *z
699719 return FAILURE ;
700720 }
701721
702- desc -> is_pipe = 1 ;
722+ desc -> type = DESCRIPTOR_TYPE_PIPE ;
703723
704724 if (strncmp (ZSTR_VAL (zmode ), "w" , 1 ) != 0 ) {
705725 desc -> parentend = newpipe [1 ];
@@ -711,17 +731,42 @@ static int set_proc_descriptor_to_pipe(descriptorspec_item *desc, zend_string *z
711731 desc -> mode_flags = O_RDONLY ;
712732 }
713733
714- #ifdef PHP_WIN32
715- /* don't let the child inherit the parent side of the pipe */
716- desc -> parentend = dup_handle (desc -> parentend , FALSE, TRUE);
734+ desc -> parentend = make_descriptor_cloexec (desc -> parentend );
717735
736+ #ifdef PHP_WIN32
718737 if (ZSTR_LEN (zmode ) >= 2 && ZSTR_VAL (zmode )[1 ] == 'b' )
719738 desc -> mode_flags |= O_BINARY ;
720739#endif
721740
722741 return SUCCESS ;
723742}
724743
744+ #ifdef PHP_WIN32
745+ #define create_socketpair (socks ) socketpair_win32(AF_INET, SOCK_STREAM, 0, (socks), 0)
746+ #else
747+ #define create_socketpair (socks ) socketpair(AF_UNIX, SOCK_STREAM, 0, (socks))
748+ #endif
749+
750+ static int set_proc_descriptor_to_socket (descriptorspec_item * desc )
751+ {
752+ php_socket_t sock [2 ];
753+
754+ if (create_socketpair (sock )) {
755+ zend_string * err = php_socket_error_str (php_socket_errno ());
756+ php_error_docref (NULL , E_WARNING , "Unable to create socket pair: %s" , ZSTR_VAL (err ));
757+ zend_string_release (err );
758+ return FAILURE ;
759+ }
760+
761+ desc -> type = DESCRIPTOR_TYPE_SOCKET ;
762+ desc -> parentend = make_descriptor_cloexec ((php_file_descriptor_t ) sock [0 ]);
763+
764+ /* Pass sock[1] to child because it will never use overlapped IO on Windows. */
765+ desc -> childend = (php_file_descriptor_t ) sock [1 ];
766+
767+ return SUCCESS ;
768+ }
769+
725770static int set_proc_descriptor_to_file (descriptorspec_item * desc , zend_string * file_path ,
726771 zend_string * file_mode )
727772{
@@ -827,6 +872,9 @@ static int set_proc_descriptor_from_array(zval *descitem, descriptorspec_item *d
827872 goto finish ;
828873 }
829874 retval = set_proc_descriptor_to_pipe (& descriptors [ndesc ], zmode );
875+ } else if (zend_string_equals_literal (ztype , "socket" )) {
876+ /* Set descriptor to socketpair */
877+ retval = set_proc_descriptor_to_socket (& descriptors [ndesc ]);
830878 } else if (zend_string_equals_literal (ztype , "file" )) {
831879 /* Set descriptor to file */
832880 if ((zfile = get_string_parameter (descitem , 1 , "file name parameter for 'file'" )) == NULL ) {
@@ -903,7 +951,7 @@ static int close_parentends_of_pipes(descriptorspec_item *descriptors, int ndesc
903951 * Also, dup() the child end of all pipes as necessary so they will use the FD
904952 * number which the user requested */
905953 for (int i = 0 ; i < ndesc ; i ++ ) {
906- if (descriptors [i ].is_pipe ) {
954+ if (descriptors [i ].type != DESCRIPTOR_TYPE_STD ) {
907955 close (descriptors [i ].parentend );
908956 }
909957 if (descriptors [i ].childend != descriptors [i ].index ) {
@@ -1194,12 +1242,13 @@ PHP_FUNCTION(proc_open)
11941242 /* Clean up all the child ends and then open streams on the parent
11951243 * ends, where appropriate */
11961244 for (i = 0 ; i < ndesc ; i ++ ) {
1197- char * mode_string = NULL ;
11981245 php_stream * stream = NULL ;
11991246
12001247 close_descriptor (descriptors [i ].childend );
12011248
1202- if (descriptors [i ].is_pipe ) {
1249+ if (descriptors [i ].type == DESCRIPTOR_TYPE_PIPE ) {
1250+ char * mode_string = NULL ;
1251+
12031252 switch (descriptors [i ].mode_flags ) {
12041253#ifdef PHP_WIN32
12051254 case O_WRONLY |O_BINARY :
@@ -1219,32 +1268,31 @@ PHP_FUNCTION(proc_open)
12191268 mode_string = "r+" ;
12201269 break ;
12211270 }
1271+
12221272#ifdef PHP_WIN32
12231273 stream = php_stream_fopen_from_fd (_open_osfhandle ((zend_intptr_t )descriptors [i ].parentend ,
12241274 descriptors [i ].mode_flags ), mode_string , NULL );
12251275 php_stream_set_option (stream , PHP_STREAM_OPTION_PIPE_BLOCKING , blocking_pipes , NULL );
12261276#else
12271277 stream = php_stream_fopen_from_fd (descriptors [i ].parentend , mode_string , NULL );
1228- # if defined(F_SETFD ) && defined(FD_CLOEXEC )
1229- /* Mark the descriptor close-on-exec, so it won't be inherited by
1230- * potential other children */
1231- fcntl (descriptors [i ].parentend , F_SETFD , FD_CLOEXEC );
1232- # endif
12331278#endif
1234- if (stream ) {
1235- zval retfp ;
1279+ } else if (descriptors [i ].type == DESCRIPTOR_TYPE_SOCKET ) {
1280+ stream = php_stream_sock_open_from_socket ((php_socket_t ) descriptors [i ].parentend , NULL );
1281+ } else {
1282+ proc -> pipes [i ] = NULL ;
1283+ }
12361284
1237- /* nasty hack; don't copy it */
1238- stream -> flags |= PHP_STREAM_FLAG_NO_SEEK ;
1285+ if ( stream ) {
1286+ zval retfp ;
12391287
1240- php_stream_to_zval ( stream , & retfp );
1241- add_index_zval ( pipes , descriptors [ i ]. index , & retfp ) ;
1288+ /* nasty hack; don't copy it */
1289+ stream -> flags |= PHP_STREAM_FLAG_NO_SEEK ;
12421290
1243- proc -> pipes [ i ] = Z_RES ( retfp );
1244- Z_ADDREF ( retfp );
1245- }
1246- } else {
1247- proc -> pipes [ i ] = NULL ;
1291+ php_stream_to_zval ( stream , & retfp );
1292+ add_index_zval ( pipes , descriptors [ i ]. index , & retfp );
1293+
1294+ proc -> pipes [ i ] = Z_RES ( retfp );
1295+ Z_ADDREF ( retfp ) ;
12481296 }
12491297 }
12501298
0 commit comments