@@ -276,6 +276,27 @@ rb_update_max_fd(int fd)
276276void
277277rb_maygvl_fd_fix_cloexec (int fd )
278278{
279+ #ifdef _WIN32
280+ /* Use the dedicated rb_w32_set_cloexec helper instead of the fcntl
281+ * path below, so that both the OS-level HANDLE_FLAG_INHERIT bit and
282+ * the CRT _osfile FNOINHERIT bit are kept in sync. The standard
283+ * handles (0, 1, 2) stay inheritable, while every other fd is made
284+ * non-inheritable (close-on-exec) by default. This matches the
285+ * assumption elsewhere (notably the lpReserved2 fd-inheritance
286+ * machinery) that a fd is inherited by a child only when
287+ * close_on_exec was explicitly cleared. */
288+ if (fd <= 2 ) {
289+ if (rb_w32_set_cloexec (fd , FALSE) != 0 )
290+ rb_bug ("rb_maygvl_fd_fix_cloexec: rb_w32_set_cloexec(%d, FALSE) failed: %s" ,
291+ fd , strerror (errno ));
292+ }
293+ else {
294+ if (rb_w32_set_cloexec (fd , TRUE) != 0 )
295+ rb_bug ("rb_maygvl_fd_fix_cloexec: rb_w32_set_cloexec(%d, TRUE) failed: %s" ,
296+ fd , strerror (errno ));
297+ }
298+ return ;
299+ #endif
279300 /* MinGW don't have F_GETFD and FD_CLOEXEC. [ruby-core:40281] */
280301#if defined(HAVE_FCNTL ) && defined(F_GETFD ) && defined(F_SETFD ) && defined(FD_CLOEXEC )
281302 int flags , flags2 , ret ;
@@ -303,6 +324,13 @@ rb_fd_fix_cloexec(int fd)
303324 rb_update_max_fd (fd );
304325}
305326
327+ /* License: Ruby's */
328+ int
329+ rb_get_max_fd (void )
330+ {
331+ return (int )max_file_descriptor ;
332+ }
333+
306334/* this is only called once */
307335static int
308336rb_fix_detect_o_cloexec (int fd )
@@ -7705,6 +7733,21 @@ pipe_open(VALUE execarg_obj, const char *modestr, enum rb_io_mode fmode,
77057733# if defined(HAVE_SPAWNVE )
77067734 if (eargp -> envp_str ) envp = (char * * )RSTRING_PTR (eargp -> envp_str );
77077735# endif
7736+ # if defined(_WIN32 )
7737+ /* On Windows, spawn through the inherit-table path so that
7738+ * close_on_exec = false fds (and any explicit fd_dup2 redirects
7739+ * set up above) propagate to the child via lpReserved2. */
7740+ struct rb_w32_spawn_actions * actions = rb_w32_build_spawn_actions (eargp );
7741+ if (args ) {
7742+ pid = rb_w32_uaspawn_inherit (P_NOWAIT ,
7743+ cmd , args , 0 , CP_UTF8 , actions );
7744+ }
7745+ else {
7746+ pid = rb_w32_uspawn_inherit (P_NOWAIT , cmd , NULL ,
7747+ CP_UTF8 , actions );
7748+ }
7749+ rb_w32_spawn_actions_destroy (actions );
7750+ # else
77087751 while ((pid = DO_SPAWN (cmd , args , envp )) < 0 ) {
77097752 /* exec failed */
77107753 switch (e = errno ) {
@@ -7717,6 +7760,7 @@ pipe_open(VALUE execarg_obj, const char *modestr, enum rb_io_mode fmode,
77177760 }
77187761 break ;
77197762 }
7763+ # endif
77207764 if (eargp )
77217765 rb_execarg_run_options (sargp , NULL , NULL , 0 );
77227766# endif
@@ -7738,9 +7782,7 @@ pipe_open(VALUE execarg_obj, const char *modestr, enum rb_io_mode fmode,
77387782
77397783 /* parent */
77407784 if (pid < 0 ) {
7741- # if defined(HAVE_WORKING_FORK )
77427785 e = errno ;
7743- # endif
77447786 close (arg .pair [0 ]);
77457787 close (arg .pair [1 ]);
77467788 if ((fmode & (FMODE_READABLE |FMODE_WRITABLE )) == (FMODE_READABLE |FMODE_WRITABLE )) {
0 commit comments